Tassonomia di errori per app business: UI coerente e monitoring
Una tassonomia di errori per app business aiuta a classificare validazione, autenticazione, rate limit e fallimenti di dipendenze in modo che alert e UI siano coerenti.

Cosa risolve una tassonomia di errori nelle app business reali
Una tassonomia di errori è un modo condiviso di nominare e raggruppare gli errori così che tutti li gestiscano nello stesso modo. Invece di avere ogni schermata e API che inventano messaggi diversi, si definisce un piccolo insieme di categorie (come validation o auth) e regole su come devono apparire all'utente e nel monitoring.
Senza questa struttura condivisa, lo stesso problema compare in forme diverse. Un campo obbligatorio mancante può risultare in “Bad Request” su mobile, “Qualcosa è andato storto” sul web e in uno stack trace nei log. Gli utenti non sanno cosa fare dopo, e i team on-call perdono tempo a indovinare se sia un errore dell'utente, un attacco o un'interruzione.
L'obiettivo è la coerenza: lo stesso tipo di errore porta allo stesso comportamento UI e allo stesso comportamento di allerta. I problemi di validazione dovrebbero indicare il campo preciso. I problemi di permesso dovrebbero bloccare l'azione ed spiegare quale accesso manca. I fallimenti di dipendenze dovrebbero offrire un retry sicuro, mentre il monitoring genera l'allarme giusto.
Un esempio realistico: un commerciale prova a creare un cliente, ma il servizio di pagamento è down. Se la tua app restituisce un generico 500, riproverà e potrebbe creare duplicati. Con una chiara categoria dependency-failure, l'UI può dire che il servizio è temporaneamente non disponibile, prevenire invii duplicati e il monitoring può avvisare il team corretto.
Questo allineamento conta soprattutto quando un backend alimenta più client. Se API, web app, mobile e strumenti interni usano le stesse categorie e codici, i fallimenti smettono di sembrare casuali.
Un modello semplice: categoria, codice, messaggio, dettagli
Le tassonomie sono più facili da mantenere quando separi quattro cose che spesso si mescolano: la categoria (che tipo di problema è), il codice (un identificatore stabile), il messaggio (testo per gli umani) e i dettagli (contesto strutturato). Lo status HTTP conta ancora, ma non dovrebbe essere tutta la storia.
La categoria risponde a: “Come devono comportarsi UI e monitoring?” Un 403 può significare “auth” in un posto, mentre un altro 403 potrebbe essere “policy” se poi aggiungi regole. La categoria riguarda il comportamento, non il trasporto.
Il codice risponde a: “Cosa è successo esattamente?” I codici dovrebbero essere stabili e noiosi. Se rinomini un pulsante o rifattorizzi un servizio, il codice non dovrebbe cambiare. Dashboard, allarmi e script di supporto dipendono da questo.
Il messaggio risponde a: “Cosa diciamo a una persona?” Decidi a chi è rivolto il messaggio. Un messaggio per l'utente dovrebbe essere breve e gentile. Un messaggio per il support può includere passi successivi. I log possono essere più tecnici.
I dettagli rispondono a: “Cosa ci serve per risolverlo?” Mantieni i dettagli strutturati così l'UI può reagire. Per un errore di form, può essere il nome del campo. Per un problema di dipendenza, può essere il nome del servizio upstream e un valore retry-after.
Ecco una forma compatta che molti team usano:
{
"category": "validation",
"code": "CUSTOMER_EMAIL_INVALID",
"message": "Enter a valid email address.",
"details": { "field": "email", "rule": "email" }
}
Man mano che le feature cambiano, mantieni le categorie piccole e stabili, e aggiungi nuovi codici invece di riusare quelli vecchi. Questo mantiene il comportamento UI, le tendenze del monitoring e le playbook di support affidabili mentre il prodotto evolve.
Categorie principali: validation, auth, rate limits, dependencies
La maggior parte delle app business può partire con quattro categorie che compaiono sempre. Se le nomini e le tratti allo stesso modo in backend, web e mobile, la UI può rispondere in modo coerente e il monitoring diventa leggibile.
Validation (atteso)
Gli errori di validazione capitano quando l'input utente o una regola di business non è rispettata. Sono normali e dovrebbero essere facili da correggere: campi obbligatori mancanti, formati non validi o regole come “lo sconto non può superare il 20%” o “il totale dell'ordine deve essere > $0”. L'UI dovrebbe evidenziare il campo o la regola esatta, non mostrare un avviso generico.
Autenticazione vs autorizzazione (atteso)
Gli errori auth si dividono generalmente in due casi: non autenticato (non loggato, sessione scaduta, token mancante) e non autorizzato (loggato ma senza permessi). Trattali diversamente. “Effettua di nuovo l'accesso” va per il primo caso. Per il secondo, evita di rivelare dettagli sensibili, ma sii chiaro: “Non hai accesso per approvare fatture.”
Rate limits (attesi, ma basati sul tempo)
Il rate limiting significa “troppe richieste, riprova più tardi.” Appare spesso durante importazioni, dashboard molto usate o retry ripetuti. Includi un suggerimento retry-after (anche solo “attendi 30 secondi”), e fai sì che l'UI rallenti invece di martellare il server.
Fallimenti di dipendenze (spesso inaspettati)
I fallimenti di dipendenze provengono da servizi upstream, timeout o outage: provider di pagamento, email/SMS, database o servizi interni. Gli utenti non possono risolverli, perciò l'UI dovrebbe offrire un fallback sicuro (salvare una bozza, riprovare più tardi, contattare supporto).
La differenza chiave è il comportamento: gli errori attesi fanno parte del flusso normale e meritano un feedback preciso; gli errori inaspettati segnalano instabilità e dovrebbero attivare allarmi, correlation ID e logging accurato.
Passo dopo passo: costruire la tua tassonomia in un workshop
Una tassonomia dovrebbe essere abbastanza piccola da ricordare, ma abbastanza rigida perché due team etichettino lo stesso problema nello stesso modo.
1) Limita il tempo e scegli un set piccolo
Inizia con un workshop di 60–90 minuti. Elenca gli errori che vedi più spesso (input errato, problemi di login, troppe richieste, outage di terze parti, bug inaspettati), poi raggruppali in 6–12 categorie che tutti possano nominare ad alta voce senza consultare un doc.
2) Concorda uno schema di codici stabile
Scegli uno schema di naming leggibile nei log e nei ticket. Mantienilo breve, evita numeri di versione e tratta i codici come permanenti una volta rilasciati. Un pattern comune è prefisso di categoria più uno slug chiaro, come AUTH_INVALID_TOKEN o DEP_PAYMENT_TIMEOUT.
Prima di terminare, decidete cosa ogni errore deve includere: categoria, codice, messaggio sicuro, dettagli strutturati e un trace o request ID.
3) Scrivi una regola semplice per categoria vs codice
I team si bloccano quando le categorie diventano un deposito generico. Una regola semplice aiuta: la categoria risponde “Come devono reagire UI e monitoring?”, il codice risponde “Cos'è successo esattamente?”. Se due fallimenti richiedono comportamenti UI diversi, non dovrebbero condividere la stessa categoria.
4) Imposta il comportamento UI predefinito per categoria
Decidi cosa vede l'utente di default. La validation evidenzia i campi. Auth porta al login o mostra un messaggio di accesso. I rate limit mostrano “riprovare in X secondi”. I fallimenti di dipendenze mostrano uno schermo calmo per il retry. Una volta definiti questi comportamenti, le nuove feature possono seguirli invece di inventare gestioni ad hoc.
5) Testa con scenari reali
Esegui cinque flussi comuni (signup, checkout, ricerca, modifica admin, upload file) ed etichetta ogni fallimento. Se il gruppo discute, di solito serve una regola più chiara, non venti codici in più.
Errori di validazione: rendili azionabili per gli utenti
La validazione è il tipo di errore che di solito vuoi mostrare immediatamente. Deve essere prevedibile: dice all'utente cosa correggere e non deve mai attivare un loop di retry.
Gli errori a livello di campo e di form sono problemi diversi. Gli errori di campo si mappano a un singolo input (email, telefono, importo). Gli errori a livello di form riguardano la combinazione di input (la data di inizio deve essere prima di quella di fine) o prerequisiti mancanti (nessun metodo di spedizione selezionato). La risposta API dovrebbe chiarire questa differenza così l'UI può reagire correttamente.
Un caso comune di regola di business è “limite di credito superato”. L'utente potrebbe aver inserito un numero valido, ma l'azione non è permessa per lo stato del conto. Tratta questo come un errore di validazione a livello di form con una ragione chiara e un suggerimento sicuro, tipo “Il tuo limite disponibile è $500. Riduci l'importo o richiedi un aumento.” Evita di esporre nomi interni come campi di database, modelli di scoring o passi dell'engines di regole.
Una risposta azionabile include di solito un codice stabile (non solo una frase in inglese), un messaggio user-friendly, puntatori ai campi opzionali per errori di campo e piccoli suggerimenti sicuri (esempi di formato, intervalli consentiti). Se serve un nome regola per gli ingegneri, mettilo nei log, non nell'UI.
Registra i fallimenti di validazione diversamente dagli errori di sistema. Vuoi abbastanza contesto per trovare pattern senza memorizzare dati sensibili. Registra user ID, request ID, il nome della regola o codice e quali campi hanno fallito. Per i valori, registra solo ciò che serve (spesso “presente/mancante” o lunghezza) e maschera tutto ciò che è sensibile.
In UI, concentrati sul correggere, non sul riprovare. Evidenzia i campi, mantieni ciò che l'utente ha digitato, scorri al primo errore e disabilita retry automatici. Gli errori di validazione non sono temporanei, quindi “riprova” è tempo perso.
Errori di autenticazione e permessi: mantieni sicurezza e chiarezza
Gli errori di autenticazione e autorizzazione somigliano agli occhi degli utenti, ma significano cose diverse per la sicurezza, il flusso UI e il monitoring. Separarli rende il comportamento coerente tra web, mobile e client API.
Unauthenticated significa che l'app non riesce a dimostrare chi è l'utente. Cause tipiche: credenziali mancanti, token non valido o sessione scaduta. Forbidden significa che l'utente è noto ma non autorizzato a eseguire l'azione.
La sessione scaduta è il caso limite più comune. Se supporti refresh token, prova un silent refresh una volta e poi riprova la richiesta originale. Se il refresh fallisce, restituisci un errore unauthenticated e manda l'utente al login. Evita loop: dopo un solo tentativo di refresh, fermati e mostra il passo successivo chiaro.
Il comportamento UI dovrebbe rimanere prevedibile:
- Unauthenticated: richiedi il login e preserva ciò che l'utente stava facendo
- Forbidden: resta sulla pagina e mostra un messaggio di accesso, più un'azione sicura come “richiedi accesso”
- Account disabilitato o revocato: effettua il logout e mostra un messaggio breve che il support può aiutare
Per l'audit, registra abbastanza informazioni per rispondere a “chi ha provato cosa e perché è stato bloccato” senza esporre segreti. Un record utile include user ID (se noto), tenant o workspace, nome dell'azione, identificatore della risorsa, timestamp, request ID e il risultato del controllo policy (allowed/denied). Tieni fuori dai log i token grezzi e le password.
Nei messaggi verso l'utente, non rivelare nomi di ruoli, regole di permessi o struttura interna delle policy. “Non hai accesso per approvare fatture” è più sicuro di “Solo FinanceAdmin può approvare fatture.”
Errori di rate limit: comportamento prevedibile sotto carico
I rate limit non sono bug. Sono un'ancora di sicurezza. Trattali come una categoria di prima classe affinché UI, log e allarmi reagiscano coerentemente quando il traffico sale.
I rate limit solitamente appaiono in poche forme: per utente (una persona che clicca troppo velocemente), per IP (molti utenti dietro la stessa rete aziendale) o per API key (un'integrazione che gira fuori controllo). La causa conta perché la soluzione è diversa.
Cosa include una buona risposta di rate-limit
I client hanno bisogno di due cose: sapere che sono limitati e quando riprovare. Restituisci HTTP 429 più un tempo di attesa chiaro (per esempio Retry-After: 30). Includi anche un codice di errore stabile (come RATE_LIMITED) così le dashboard possono raggruppare gli eventi.
Mantieni il messaggio calmo e specifico. “Too many requests” è tecnicamente vero ma non utile. “Attendi 30 secondi e riprova” imposta aspettative e riduce i clic ripetuti.
Nell'UI, previeni retry rapidi. Un pattern semplice è disabilitare l'azione per il periodo di attesa, mostrare un breve conto alla rovescia e offrire un retry sicuro quando il timer finisce. Evita frasi che facciano pensare che i dati siano stati persi.
Nel monitoring i team spesso sovrareagiscono. Non svegliare qualcuno per ogni 429. Traccia i tassi e allerta su spike anomali: un aumento improvviso per un endpoint, tenant o API key è azionabile.
Nel backend, il comportamento dovrebbe essere prevedibile. Usa exponential backoff per i retry automatici e rendi i retry idempotenti. Un’azione “Create invoice” non dovrebbe creare due fatture se la prima richiesta è andata a buon fine.
Fallimenti di dipendenze: gestire outage senza caos
I fallimenti di dipendenze sono quelli che gli utenti non possono risolvere con un input migliore. L'utente ha fatto tutto correttamente, ma un gateway di pagamento è andato in timeout, una connessione DB è caduta o un servizio upstream ha restituito un 5xx. Trattali come una categoria separata così UI e monitoring si comportano in modo prevedibile.
Inizia nominando le forme comuni di errore: timeout, errore di connessione (DNS, TLS, refused) e upstream 5xx (bad gateway, service unavailable). Anche se non puoi conoscere la causa esatta, puoi catturare cosa è successo e rispondere in modo coerente.
Retry o fallire rapidamente
I retry aiutano per piccoli disturbi, ma possono anche peggiorare un outage. Usa regole semplici così ogni team prende la stessa decisione.
- Riprova quando l'errore è probabilmente temporaneo: timeout, reset di connessione, 502/503
- Fallisci subito per casi permanenti o causati dall'utente: 4xx dal dependency, credenziali non valide, risorsa mancante
- Limita i retry (per esempio 2–3 tentativi) e aggiungi un piccolo backoff
- Non ritentare mai azioni non idempotenti a meno che non ci sia una chiave di idempotenza
Comportamento UI e fallback sicuri
Quando una dipendenza fallisce, indica cosa può fare l'utente dopo senza incolparlo: “Problema temporaneo. Riprova più tardi.” Se esiste un fallback sicuro, offrilo. Esempio: se Stripe è down, permetti di salvare l'ordine come “Pagamento in sospeso” e invia una conferma via email invece di perdere il carrello.
Proteggi anche dagli invii doppi. Se l'utente tocca “Paga” due volte durante una risposta lenta, il sistema dovrebbe rilevarlo. Usa chiavi di idempotenza per i flussi create-and-charge, o controlli di stato come “ordine già pagato” prima di rieseguire l'azione.
Per il monitoring, registra campi che rispondono rapidamente a una domanda: “Quale dipendenza sta fallendo e quanto è grave?” Cattura nome della dipendenza, endpoint o operazione, durata e risultato finale (timeout, connect, upstream 5xx). Questo rende alert e dashboard significativi invece che rumorosi.
Allinea monitoring e UI su tutti i canali
Le tassonomie funzionano solo quando ogni canale parla la stessa lingua: API, UI web, mobile e i log. Altrimenti lo stesso problema appare come cinque messaggi diversi e nessuno sa se è errore utente o un vero outage.
Tratta gli status HTTP come un livello secondario. Aiutano proxy e comportamenti base dei client, ma la tua categoria e il codice devono portare il significato. Un timeout di dipendenza può ancora essere un 503, ma la categoria dice all'UI di offrire “Riprova” e al monitoring di pageare l'on-call.
Fai restituire a ogni API una forma di errore standard, anche quando la fonte è diversa (database, modulo auth, API di terze parti). Una struttura semplice mantiene coerenti la gestione UI e le dashboard:
{
"category": "dependency",
"code": "PAYMENTS_TIMEOUT",
"message": "Payment service is not responding.",
"details": {"provider": "stripe"},
"correlation_id": "9f2c2c3a-6a2b-4a0a-9e9d-0b0c0c8b2b10"
}
I correlation ID sono il ponte tra “un utente ha visto un errore” e “possiamo tracciarlo”. Mostra correlation_id nell'UI (un pulsante copia aiuta), e registralo sempre nel backend così puoi seguire una richiesta attraverso i servizi.
Concorda cosa è sicuro mostrare in UI vs solo nei log. Una suddivisione pratica: UI ottiene categoria, messaggio chiaro e passo successivo; i log ottengono dettagli tecnici e contesto della richiesta; entrambi condividono correlation_id e codice di errore stabile.
Checklist rapida per un sistema di errori coerente
La coerenza è noiosa nel modo migliore: ogni canale si comporta allo stesso modo e il monitoring dice la verità.
Controlla prima il backend, inclusi job in background e webhook. Se un campo è opzionale, la gente lo salterà e la coerenza si rompe.
- Ogni errore include categoria, codice stabile, messaggio sicuro per l'utente e trace ID.
- I problemi di validazione sono attesi, quindi non attivano pagine di allerta.
- I problemi di auth e permessi sono tracciati per pattern di sicurezza, ma non trattati come outage.
- Le risposte di rate limit includono un hint di retry (per esempio, secondi da aspettare) e non generano spam di allarmi.
- I fallimenti di dipendenze includono il nome della dipendenza più dettaglii su timeout o status.
Poi controlla le regole UI. Ogni categoria dovrebbe mappare a un comportamento di schermata prevedibile così gli utenti non devono indovinare cosa fare dopo: la validation evidenzia campi, auth porta al login o mostra accesso, i rate limit mostrano un'attesa calma, i fallimenti di dipendenze offrono retry e un fallback quando possibile.
Un test semplice è provocare uno errore per ogni categoria in staging e verificare che il risultato sia lo stesso su web app, mobile e pannello admin.
Errori comuni e prossimi passi pratici
Il modo più rapido per rompere un sistema di errori è trattarlo come un ripensamento. Team diversi finiscono per usare parole diverse, codici diversi e comportamenti UI diversi per lo stesso problema. Il lavoro sulla tassonomia ripaga quando resta consistente.
Schemi di errore comuni:
- Esposizione di testi di eccezioni interne agli utenti. Confonde e può rivelare dettagli sensibili.
- Etichettare ogni 4xx come “validation.” La mancanza di permessi non è la stessa cosa di un campo mancante.
- Inventare nuovi codici per ogni feature senza revisione. Ti ritrovi con 200 codici che significano le stesse 5 cose.
- Ritentare i fallimenti sbagliati. Ritentare un errore di permesso o un'email non valida crea solo rumore.
Un esempio semplice: un commerciale invia un form “Create customer” e riceve un 403. Se l'UI tratta tutti i 4xx come validation, evidenzierà campi a caso e chiederà di “correggere input” invece di dire che serve accesso. Il monitoring mostrerà uno spike di “validation issues” quando il problema reale è il ruolo.
Prossimi passi pratici che entrano in un breve workshop: scrivi un documento di una pagina (categorie, quando usarle, 5–10 codici canonici), definisci regole sui messaggi (cosa vede l'utente vs cosa va nei log), aggiungi una gate di revisione leggera per i nuovi codici, imposta regole di retry per categoria, poi implementa end-to-end (risposta backend, mapping UI e dashboard di monitoring).
Se stai costruendo con AppMaster (appmaster.io), aiuta centralizzare queste regole in un unico posto così lo stesso comportamento di categoria e codice si propaga tra backend, web app e app native.
FAQ
Inizia quando lo stesso backend serve più di un client (web, mobile, strumenti interni), o quando support e on-call continuano a chiedere: “È un errore dell'utente o del sistema?” Una tassonomia paga rapidamente quando hai flussi ripetuti come registrazione, checkout, importazioni o modifiche amministrative, dove la gestione coerente è importante.
Un buon punto di partenza sono 6–12 categorie che le persone possono ricordare senza consultare documentazione. Mantieni le categorie stabili e ampie (es. validation, auth, rate_limit, dependency, conflict, internal) e usa il codice per esprimere la situazione specifica, non una nuova categoria.
La categoria guida il comportamento, il codice identifica la situazione esatta. La categoria dice all'UI e al monitoring cosa fare (evidenziare campi, chiedere di effettuare il login, rallentare, offrire retry), mentre il codice rimane stabile per dashboard, allarmi e script di supporto anche se il testo in UI cambia.
Tratta i messaggi come contenuto, non come identificatori. Restituisci un messaggio breve e sicuro per l'utente nell'UI e usa il codice stabile per raggruppare e automatizzare. Se serve un testo più tecnico, lascialo nei log e collegalo allo stesso correlation ID.
Includi una categoria, un codice stabile, un messaggio sicuro per l'utente, dettagli strutturati e un correlation o request ID. I dettagli devono essere modellati per permettere al client di agire (quale campo ha fallito, quanto aspettare), senza riversare testi di eccezioni grezzi.
Restituisci puntatori a livello di campo quando possibile, così l'UI può evidenziare l'input esatto e mantenere ciò che l'utente ha scritto. Usa un errore a livello di form quando il problema riguarda una combinazione di campi o una regola di business (es. intervalli di date o limiti di credito), in modo che l'UI non indovini il campo sbagliato.
Unauthenticated significa che l'utente non è loggato o il token/sessione non è valido: l'UI dovrebbe mandarlo al login e preservare il compito. Forbidden significa che è autenticato ma gli manca il permesso: l'UI dovrebbe restare sulla pagina e mostrare un messaggio di accesso senza rivelare ruoli o dettagli di policy.
Restituisci un tempo di attesa esplicito (per esempio un valore retry-after) e mantieni il codice stabile così i client possono implementare backoff in modo coerente. In UI, disabilita i clic ripetuti e mostra un passo successivo chiaro: i retry rapidi automatici in genere peggiorano il rate limiting.
Effettua retry solo quando l'errore è probabilmente temporaneo (timeout, reset di connessione, upstream 502/503) e limita i tentativi con un piccolo backoff. Per azioni non idempotenti, richiedi una chiave di idempotenza o un controllo di stato, altrimenti un retry può creare duplicati se il primo tentativo è riuscito.
Mostra l'ID di correlazione all'utente (così il supporto può richiederlo) e registralo sempre lato server con codice e dettagli chiave. Questo permette di tracciare un singolo fallimento attraverso servizi e client; nei progetti AppMaster, centralizzare questa struttura aiuta a mantenere comportamento allineato tra backend, web e mobile nativo.


