Taxonomia de erros para apps empresariais: UI e monitoramento consistentes
A taxonomia de erros para apps empresariais ajuda a classificar validação, autenticação, limites de taxa e falhas de dependências para que alertas e respostas de UI permaneçam consistentes.

O que uma taxonomia de erros resolve em apps empresariais reais
Uma taxonomia de erros é uma forma compartilhada de nomear e agrupar erros para que todos os tratem da mesma forma. Em vez de cada tela e API inventar suas próprias mensagens, você define um conjunto pequeno de categorias (como validação ou auth) e regras sobre como elas aparecem para usuários e no monitoramento.
Sem essa estrutura compartilhada, o mesmo problema aparece em formas diferentes. Um campo obrigatório ausente pode aparecer como “Bad Request” no mobile, “Algo deu errado” na web e um stack trace nos logs. Usuários não sabem o que fazer em seguida, e equipes on-call perdem tempo tentando adivinhar se é erro do usuário, um ataque ou uma queda de serviço.
O objetivo é consistência: o mesmo tipo de erro leva ao mesmo comportamento de UI e ao mesmo comportamento de alerta. Problemas de validação devem apontar o campo exato. Questões de permissão devem impedir a ação e explicar qual acesso falta. Falhas em dependências devem oferecer uma tentativa segura, enquanto o monitoramento gera o alarme correto.
Um exemplo realista: um representante de vendas tenta criar um cadastro de cliente, mas o serviço de pagamentos está fora do ar. Se seu app retorna um 500 genérico, ele vai tentar novamente e pode criar duplicatas depois. Com uma categoria clara de falha em dependência, a UI pode avisar que o serviço está temporariamente indisponível, prevenir envios duplicados e o monitoramento pode acionar a equipe certa.
Esse alinhamento importa muito quando um backend alimenta múltiplos clientes. Se a API, o app web, o mobile e as ferramentas internas usam as mesmas categorias e códigos, as falhas deixam de parecer aleatórias.
Um modelo simples: categoria, código, mensagem, detalhes
Taxonomias são mais fáceis de manter quando você separa quatro coisas que frequentemente se misturam: a categoria (que tipo de problema é), o código (um identificador estável), a mensagem (texto para humanos) e os detalhes (contexto estruturado). O status HTTP ainda importa, mas não deve ser a história inteira.
Categoria responde: “Como a UI e o monitoramento devem se comportar?” Um 403 pode significar “auth” em um lugar, enquanto outro 403 pode ser “policy” se você depois adicionar regras. Categoria trata de comportamento, não de transporte.
Código responde: “O que exatamente aconteceu?” Códigos devem ser estáveis e sem graça. Se você renomear um botão ou refatorar um serviço, o código não deve mudar. Dashboards, alertas e scripts de suporte dependem disso.
Mensagem responde: “O que dizemos para a pessoa?” Decida para quem a mensagem é. Uma mensagem para usuário deve ser curta e gentil. Uma mensagem para suporte pode incluir próximos passos. Logs podem ser mais técnicos.
Detalhes respondem: “O que precisamos para consertar?” Mantenha detalhes estruturados para que a UI possa reagir. Para um erro de formulário, isso pode ser nomes de campos. Para uma dependência, pode ser o nome do serviço upstream e um valor retry-after.
Aqui está uma forma compacta que muitas equipes usam:
{
"category": "validation",
"code": "CUSTOMER_EMAIL_INVALID",
"message": "Enter a valid email address.",
"details": { "field": "email", "rule": "email" }
}
À medida que os recursos mudam, mantenha as categorias pequenas e estáveis, e adicione novos códigos em vez de reusar os antigos. Isso mantém o comportamento da UI, as tendências do monitoramento e os playbooks de suporte confiáveis conforme o produto evolui.
Categorias principais: validation, auth, rate limits, dependencies
A maioria dos apps empresariais pode começar com quatro categorias que aparecem em todos os lugares. Se você nomeá-las e tratá-las da mesma forma entre backend, web e mobile, sua UI pode responder de forma consistente e seu monitoramento fica legível.
Validação (esperado)
Erros de validação ocorrem quando a entrada do usuário ou uma regra de negócio falha. Esses são normais e devem ser fáceis de corrigir: campos obrigatórios ausentes, formatos inválidos ou regras como “desconto não pode exceder 20%” ou “o total do pedido deve ser > $0”. A UI deve destacar o campo ou a regra exata, não mostrar um alerta genérico.
Autenticação vs autorização (esperado)
Erros de auth geralmente se dividem em dois casos: não autenticado (não logado, sessão expirada, token ausente) e não autorizado (logado, mas sem permissão). Trate-os de forma diferente. “Por favor, entre novamente” cabe ao primeiro caso. No segundo, evite revelar detalhes sensíveis, mas seja claro: “Você não tem acesso para aprovar faturas.”
Limites de taxa (esperado, mas baseado em tempo)
Rate limiting significa “muitas requisições, tente novamente mais tarde.” Frequentemente aparece durante importações, dashboards congestionados ou retries repetidos. Inclua uma dica retry-after (mesmo que seja só “espere 30 segundos”) e faça a UI reduzir em vez de bater no servidor.
Falhas de dependência (frequentemente inesperado)
Falhas de dependência vêm de serviços upstream, timeouts ou outages: provedores de pagamento, email/SMS, bancos de dados ou serviços internos. Usuários não conseguem consertar isso, então a UI deve oferecer um fallback seguro (salvar rascunho, tentar mais tarde, contatar suporte).
A diferença chave é o comportamento: erros esperados fazem parte do fluxo normal e merecem feedback preciso; erros inesperados sinalizam instabilidade e devem disparar alertas, IDs de correlação e logging cuidadoso.
Passo a passo: construa sua taxonomia em um workshop
Uma taxonomia deve ser pequena o suficiente para memorizar, mas rígida o bastante para que duas equipes rotulem o mesmo problema da mesma forma.
1) Timebox e escolha um conjunto pequeno
Comece com um workshop de 60 a 90 minutos. Liste os erros mais comuns (entrada inválida, problemas de login, muitas requisições, quedas de terceiros, bugs inesperados), então compacte-os em 6 a 12 categorias que todos possam pronunciar sem consultar um doc.
2) Combine um esquema de códigos estável
Escolha um padrão de nomeação que continue legível em logs e tickets. Mantenha curto, evite números de versão e trate códigos como permanentes após o lançamento. Um padrão comum é prefixo de categoria mais um slug claro, como AUTH_INVALID_TOKEN ou DEP_PAYMENT_TIMEOUT.
Antes de sair da sala, decida o que todo erro deve incluir: categoria, código, mensagem segura, detalhes estruturados e um trace ou request ID.
3) Escreva uma regra para categoria vs código
Equipes emperram quando categorias viram depósito de tudo. Uma regra simples ajuda: categoria responde “Como a UI e o monitoramento reagem?”, código responde “O que exatamente aconteceu?”. Se duas falhas precisam de comportamento de UI diferente, não devem compartilhar categoria.
4) Defina comportamento padrão de UI por categoria
Decida o que os usuários veem por padrão. Validação destaca campos. Auth envia para login ou mostra mensagem de acesso. Rate limits exibem “tente novamente em X segundos”. Falhas de dependência mostram uma tela de retry tranquila. Com esses padrões, novos recursos podem segui-los em vez de inventar tratamentos pontuais.
5) Teste com cenários reais
Execute cinco fluxos comuns (cadastro, checkout, busca, edição administrativa, upload de arquivo) e rotule cada falha. Se o grupo discutir muito, geralmente você precisa de uma regra mais clara, não de vinte novos códigos.
Erros de validação: torne-os acionáveis para usuários
Validação é o tipo de falha que normalmente você quer mostrar imediatamente. Deve ser previsível: diz ao usuário o que corrigir e nunca dispara um loop de retry.
Erros a nível de campo e a nível de formulário são problemas diferentes. Erros de campo mapeiam para um input (email, telefone, valor). Erros de formulário tratam da combinação de inputs (data inicial deve ser antes da data final) ou pré-requisitos ausentes (método de envio não selecionado). Sua resposta de API deve deixar essa diferença clara para que a UI reaja corretamente.
Uma falha comum de regra de negócio é “limite de crédito excedido.” O usuário pode ter inserido um número válido, mas a ação não é permitida com base no status da conta. Trate isso como erro de validação a nível de formulário com motivo claro e uma dica segura, como “Seu limite disponível é $500. Reduza o valor ou solicite aumento.” Evite expor nomes internos como campos de banco, modelos de pontuação ou passos do motor de regras.
Uma resposta acionável geralmente inclui um código estável (não apenas uma frase em inglês), uma mensagem amigável para o usuário, ponteiros de campo opcionais para erros a nível de campo e pequenas dicas seguras (exemplos de formato, faixas permitidas). Se precisar de um nome de regra para engenheiros, coloque-o nos logs, não na UI.
Registre falhas de validação de forma diferente de erros de sistema. Você quer contexto suficiente para depurar padrões sem armazenar dados sensíveis. Registre ID do usuário, request ID, o nome ou código da regra e quais campos falharam. Para valores, registre apenas o necessário (frequentemente “presente/ausente” ou comprimento) e mascare tudo que for sensível.
Na UI, foque em corrigir, não em tentar novamente. Destaque campos, mantenha o que o usuário digitou, role até o primeiro erro e desative retries automáticos. Erros de validação não são temporários, então “tente novamente” perde tempo.
Erros de auth e permissão: mantenha segurança e clareza
Falhas de autenticação e autorização parecem semelhantes para usuários, mas significam coisas diferentes para segurança, fluxo de UI e monitoramento. Separá-las torna o comportamento consistente entre web, mobile e clientes de API.
Não autenticado significa que o app não consegue provar quem é o usuário. Causas típicas são credenciais ausentes, token inválido ou sessão expirada. Forbidden significa que o usuário é conhecido, mas não autorizado a executar a ação.
Sessão expirada é o caso de borda mais comum. Se você suporta refresh tokens, tente um refresh silencioso uma vez e então retry na requisição original. Se o refresh falhar, retorne um erro de não autenticado e envie o usuário para login novamente. Evite loops: depois de uma tentativa de refresh, pare e mostre o próximo passo claro.
Comportamento de UI deve ser previsível:
- Não autenticado: peça para entrar e preserve a tarefa do usuário
- Forbidden: permaneça na página e mostre uma mensagem de acesso, além de uma ação segura como “solicitar acesso”
- Conta desativada ou revogada: faça logout e mostre uma mensagem curta dizendo que o suporte pode ajudar
Para auditoria, registre informações suficientes para responder “quem tentou o quê e por que foi bloqueado” sem expor segredos. Um registro útil inclui ID do usuário (se conhecido), tenant ou workspace, nome da ação, identificador do recurso, timestamp, request ID e o resultado da checagem de política (allowed/denied). Mantenha tokens brutos e senhas fora dos logs.
Em mensagens para usuários, não revele nomes de papéis, regras de permissão ou estrutura interna de políticas. “Você não tem acesso para aprovar faturas” é mais seguro que “Apenas FinanceAdmin pode aprovar faturas.”
Erros de limite de taxa: comportamento previsível sob carga
Rate limits não são bugs. São um trilho de segurança. Trate-os como uma categoria de primeira classe para que UI, logs e alertas reajam de forma consistente quando o tráfego aumentar.
Rate limits aparecem em algumas formas: por usuário (uma pessoa clicando rápido), por IP (muitos usuários atrás de uma rede de escritório) ou por chave de API (uma integração rodando sem controle). A causa importa porque o conserto é diferente.
O que uma boa resposta de rate-limit inclui
Clientes precisam de duas coisas: que estão limitados, e quando tentar novamente. Retorne HTTP 429 mais um tempo claro de espera (por exemplo, Retry-After: 30). Também inclua um código de erro estável (como RATE_LIMITED) para que dashboards agrupem eventos.
Mantenha a mensagem calma e específica. “Muitas requisições” é tecnicamente verdadeiro, mas pouco útil. “Por favor, espere 30 segundos e tente novamente” define expectativas e reduz cliques repetidos.
Na UI, previna retries rápidos. Um padrão simples é desabilitar a ação pelo período de espera, mostrar uma contagem regressiva curta e oferecer uma tentativa segura quando o timer terminar. Evite mensagens que façam o usuário pensar que os dados foram perdidos.
No monitoramento, equipes costumam reagir demais. Não acione page para cada 429. Monitore taxas e alerte em picos incomuns: um salto repentino por endpoint, tenant ou chave de API é acionável.
No backend, o comportamento também deve ser previsível. Use backoff exponencial para retries automáticos e faça retries idempotentes. Uma ação “Criar fatura” não deve criar duas faturas se a primeira requisição realmente tiver sido bem-sucedida.
Falhas de dependência: lidar com outages sem caos
Falhas de dependência são aquelas que usuários não conseguem consertar com melhor entrada. O usuário fez tudo certo, mas uma gateway de pagamento deu timeout, a conexão ao banco caiu ou um serviço upstream retornou 5xx. Trate isso como uma categoria separada para que UI e monitoramento reajam de forma previsível.
Comece nomeando as formas comuns de falha: timeout, erro de conexão (DNS, TLS, conexão recusada) e upstream 5xx (bad gateway, service unavailable). Mesmo que você não saiba a causa raiz, pode capturar o que aconteceu e responder de forma consistente.
Retry vs fail fast
Retries ajudam em pequenos instantes, mas também podem piorar um outage. Use regras simples para que toda equipe tome a mesma decisão.
- Tente novamente quando o erro for provavelmente temporário: timeouts, resets de conexão, 502/503
- Falhe rápido para casos permanentes ou causados pelo usuário: 4xx do dependency, credenciais inválidas, recurso ausente
- Limite retries (por exemplo 2 a 3 tentativas) e adicione um pequeno backoff
- Nunca faça retry em ações não idempotentes a menos que tenha uma chave de idempotência
Comportamento de UI e fallbacks seguros
Quando uma dependência falha, diga o que o usuário pode fazer a seguir sem culpá-lo: “Problema temporário. Por favor, tente novamente.” Se houver um fallback seguro, ofereça-o. Exemplo: se Stripe estiver fora do ar, permita salvar o pedido como “Pagamento pendente” e enviar uma confirmação por email em vez de perder o carrinho.
Também proteja usuários de envios duplos. Se o usuário tocar “Pagar” duas vezes durante uma resposta lenta, seu sistema deve detectar isso. Use chaves de idempotência para fluxos de criar-e-cobrar, ou verificações de estado como “pedido já pago” antes de executar a ação novamente.
Para monitoramento, registre campos que respondam rápido a uma pergunta: “Qual dependência está falhando e quão grave é?” Capture nome da dependência, endpoint ou operação, duração e resultado final (timeout, connect, upstream 5xx). Isso torna alertas e dashboards significativos em vez de ruidosos.
Faça monitoramento e UI consistentes entre canais
Taxonomias só funcionam quando todo canal fala a mesma linguagem: a API, a UI web, o app mobile e seus logs. Caso contrário, o mesmo problema aparece como cinco mensagens diferentes, e ninguém sabe se é erro do usuário ou uma queda real.
Trate códigos de status HTTP como uma camada secundária. Eles ajudam proxies e comportamento básico dos clientes, mas sua categoria e código devem carregar o significado. Um timeout de dependência ainda pode ser 503, mas a categoria indica à UI para oferecer “Tentar novamente” e ao monitoramento para alertar o on-call.
Faça com que cada API retorne uma forma padrão de erro, mesmo quando a origem for diferente (banco, módulo de auth, API de terceiro). Uma forma simples como esta mantém o tratamento da UI e os dashboards consistentes:
{
"category": "dependency",
"code": "PAYMENTS_TIMEOUT",
"message": "Payment service is not responding.",
"details": {"provider": "stripe"},
"correlation_id": "9f2c2c3a-6a2b-4a0a-9e9d-0b0c0c8b2b10"
}
Correlation IDs são a ponte entre “um usuário viu um erro” e “podemos traçar isso”. Mostre o correlation_id na UI (um botão de copiar ajuda) e sempre registre no backend para poder seguir uma requisição entre serviços.
Combine o que é seguro mostrar na UI vs o que fica apenas nos logs. Uma divisão prática é: a UI recebe categoria, uma mensagem clara e um próximo passo; os logs recebem detalhes técnicos e contexto de requisição; ambos compartilham correlation_id e o código de erro estável.
Checklist rápido para um sistema de erros consistente
Consistência é chata do melhor modo: todo canal se comporta igual e o monitoramento diz a verdade.
Verifique o backend primeiro, incluindo jobs em background e webhooks. Se algum campo for opcional, as pessoas vão pular e a consistência quebra.
- Todo erro inclui categoria, código estável, mensagem segura para usuário e um trace ID.
- Problemas de validação são esperados, então não disparam paging alerts.
- Erros de auth e permissão são rastreados para padrões de segurança, mas não tratados como outages.
- Respostas de rate limit incluem uma dica de retry (por exemplo, segundos a esperar) e não geram alertas em excesso.
- Falhas de dependência incluem o nome da dependência mais detalhes de timeout ou status.
Depois verifique regras de UI. Cada categoria deve mapear para um comportamento previsível de tela para que usuários não precisem adivinhar o que fazer: validação destaca campos, auth pede login ou mostra acesso, rate limits exibem uma espera calma, falhas de dependência oferecem retry e um fallback quando possível.
Um teste simples é provocar um erro de cada categoria em staging e verificar se o resultado é o mesmo no app web, mobile e no painel administrativo.
Erros comuns e próximos passos práticos
A maneira mais rápida de quebrar um sistema de erros é tratá-lo como algo secundário. Equipes diferentes acabam usando palavras, códigos e comportamentos de UI distintos para o mesmo problema. O trabalho de taxonomia vale quando é mantido consistente.
Padrões de falha comuns:
- Vazamento de texto interno de exceção para usuários. Confunde pessoas e pode expor dados sensíveis.
- Rotular todo 4xx como “validation.” Falta de permissão não é a mesma coisa que campo ausente.
- Inventar novos códigos por recurso sem revisão. Você acaba com 200 códigos que significam as mesmas 5 coisas.
- Repetir retries nos erros errados. Tentar novamente um erro de permissão ou um email inválido só gera ruído.
Um exemplo simples: um representante envia um formulário “Criar cliente” e recebe um 403. Se a UI tratar todo 4xx como validação, vai destacar campos aleatórios e pedir para “corrigir inputs” em vez de dizer que ele precisa de acesso. O monitoramento então mostra um pico em “problemas de validação” quando o real problema são papéis.
Próximos passos práticos que cabem em um workshop curto: escreva um doc de uma página com a taxonomia (categorias, quando usá-las, 5 a 10 códigos canônicos), defina regras de mensagem (o que o usuário vê vs o que vai para logs), adicione um gate leve de revisão para novos códigos, defina regras de retry por categoria e implemente ponta a ponta (resposta do backend, mapeamento na UI e dashboards de monitoramento).
Se você está construindo com AppMaster (appmaster.io), isso ajuda a centralizar essas regras em um só lugar para que a mesma categoria e comportamento de código se propaguem pelo backend, app web e apps nativos.
FAQ
Comece quando o mesmo backend serve mais de um cliente (web, mobile, ferramentas internas), ou quando suporte e on-call continuam perguntando “isso é erro do usuário ou problema do sistema?” Uma taxonomia compensa rapidamente quando você tem fluxos repetidos como cadastro, checkout, importações ou edições administrativas onde o tratamento consistente importa.
Um bom padrão é começar com 6–12 categorias que as pessoas consigam lembrar sem consultar docs. Mantenha as categorias estáveis e amplas (como validation, auth, rate_limit, dependency, conflict, internal) e expresse a situação específica com um código, não com uma nova categoria.
Categoria guia o comportamento, código identifica a situação exata. A categoria diz à UI e ao monitoramento o que fazer (destacar campos, pedir login, desacelerar, oferecer retry), enquanto o código permanece estável para dashboards, alertas e scripts de suporte mesmo que o texto da UI mude.
Trate mensagens como conteúdo, não identificadores. Retorne uma mensagem curta e segura para o usuário na UI, e use o código estável para agrupamento e automação. Se precisar de uma redação mais técnica, mantenha-a nos logs e amarre ao mesmo correlation ID.
Inclua uma categoria, um código estável, uma mensagem segura para o usuário, detalhes estruturados e um correlation ID ou request ID. Os detalhes devem ser modelados para o cliente agir, como qual campo falhou ou quanto tempo esperar, sem despejar texto bruto de exceção.
Retorne apontadores a nível de campo quando possível, para que a UI possa destacar o input exato e preservar o que o usuário digitou. Use um erro a nível de formulário quando o problema for sobre a combinação de inputs ou uma regra de negócio (intervalos de data, limites de crédito), assim a UI não tenta adivinhar o campo errado.
Unauthenticated significa que o usuário não está logado ou o token/sessão é inválido; a UI deve enviar para o login e preservar a tarefa. Forbidden significa que está logado mas sem permissão; a UI deve permanecer na página e mostrar uma mensagem de acesso sem revelar detalhes sensíveis sobre papéis ou políticas.
Retorne um tempo explícito de espera (por exemplo, um valor retry-after) e mantenha o código estável para que clientes implementem backoff de forma consistente. Na UI, desative cliques repetidos e mostre um próximo passo claro, porque retries automáticos rápidos normalmente pioram a limitação de taxa.
Retry apenas quando a falha for provavelmente temporária (timeouts, resets de conexão, upstream 502/503) e limite o número de tentativas com um pequeno backoff. Para ações não idempotentes, exija uma chave de idempotência ou uma verificação de estado; caso contrário, um retry pode criar duplicados se a primeira tentativa tiver realmente sido bem-sucedida.
Mostre o correlation ID ao usuário (para que o suporte possa solicitá-lo) e sempre registre-o no servidor junto com o código e detalhes-chave. Isso permite traçar uma falha entre serviços e clientes; em projetos AppMaster, centralizar essa forma em um só lugar ajuda a alinhar comportamento de backend, web e mobile nativo.


