Substitua campos de status em string por enums para evitar erros de digitação em workflows
Substitua campos de status em string por enums e evite que pequenas diferenças, como canceled vs cancelled, quebrem fluxos de pedido, aprovação e pagamento.

Por que status em string quebram workflows tão fácil
Campos de status em string parecem inocentes porque ficam fáceis de ler em logs e bancos. O problema é que são texto livre, então o código não te protege contra pequenas diferenças como canceled vs cancelled, Paid vs paid ou até um espaço no final.
Um único erro de digitação basta para pular um ramo crítico. Imagine um fluxo de checkout que deveria reembolsar um pagamento e parar o fulfillment quando um pedido é cancelado:
if (order.status === "canceled") {
refund(order.paymentId)
stopFulfillment(order.id)
sendCancelEmail(order.customerEmail)
}
Se uma parte do app escreve cancelled, essa condição nunca roda. Nada quebra visivelmente. O pedido simplesmente segue pelo caminho errado, e você só percebe mais tarde quando um cliente é cobrado, o depósito envia o pacote ou um e-mail não é enviado.
Esses bugs passam despercebidos em revisões por uma razão simples: strings não mostram um “conjunto permitido”. Quem revisa vê uma checagem de status e assume que o valor é válido. Mesmo que alguém note a grafia, não é óbvio se o resto do sistema usa a variante americana ou britânica.
Testes também costumam perder isso. Desenvolvedores tendem a copiar a mesma string no teste e no código, então o teste passa mesmo quando dados reais são inconsistentes.
O estrago aparece onde workflows têm consequências reais:
- Pagamentos: reembolsos não disparam, tentativas repetidas acontecem quando deveriam parar
- Aprovações: requisições ficam presas em “pending” para sempre, ou são aprovadas por engano
- E-mails e notificações: a mensagem errada é enviada, ou nada é enviado
- Fulfillment e acesso: envio continua, assinaturas permanecem ativas, contas não são bloqueadas
Ao substituir campos de status em string por enums, você muda quem é responsável pela correção. Em vez de cada desenvolvedor lembrar a grafia exata, o compilador (ou pelo menos o verificador de tipos) aplica uma lista única de status válidos.
Esse refactor não corrige regras de negócio ruins, condições de corrida ou trilhas de auditoria ausentes por si só. Ele apenas torna mais difícil escrever “estados impossíveis”, então o restante do código de workflow fica mais confiável.
Se você herdou código gerado por IA, strings de status são um dos pontos de falha silenciosa mais comuns. É normal encontrar erros de digitação espalhados pela UI, handlers de API e jobs em background.
O que enums consertam (e o que não consertam)
Um enum é uma lista nomeada de valores permitidos. Em vez de salvar uma string livre como pending ou cancelled, você escolhe entre um conjunto fixo como PENDING, PAID, CANCELED. O código trata esses como as únicas opções válidas.
O maior ganho é ter uma fonte única de verdade para os status existentes. Você não precisa lembrar se é cancelled, canceled, CANCELLED ou order_cancelled. O enum torna o conjunto permitido explícito, e qualquer outro valor é inválido.
Enums também ajudam a falhar cedo. Com strings, um erro de digitação pode chegar à produção e só quebrar um fluxo em um caminho raro. Com enums, muitos erros aparecem antes:
- Seu editor e compilador podem autocompletar e sinalizar valores desconhecidos
- Testes unitários falham exatamente onde um status inválido é atribuído
- Declarações switch e match podem avisar quando você esqueceu de tratar um status novo
- Schemas de API e validadores podem rejeitar entradas ruins imediatamente
O que enums não fazem é resolver regras de negócio pouco claras. Se a equipe não concorda quando um pedido deve ser ON_HOLD vs PENDING, um enum não vai resolver. Também não vai consertar um ciclo de vida confuso onde várias partes do app definem status de formas conflitantes. Enums tornam esses problemas mais visíveis, mas você ainda precisa de regras claras e de um dono responsável.
Valores armazenados vs rótulos de exibição
Um erro comum é misturar o que você armazena com o que exibe.
Valores armazenados devem ser estáveis e sem frescura, porque vão acabar no banco, logs e integrações. Rótulos de exibição podem ser amistosos e mudar sem quebrar nada.
Por exemplo, armazene CANCELED como o valor do enum, mas exiba “Canceled” na UI. Se depois quiser mostrar “Cancelled” para usuários no Reino Unido, isso deve ser apenas uma mudança de rótulo na UI, sem migração de banco.
Essa distinção importa ainda mais durante a limpeza de código gerado por IA, onde protótipos frequentemente usam strings da UI como valores de status. Separar enums internos do texto para humanos ajuda a impedir que o próximo erro de digitação vire um incidente em produção.
Comece mapeando seus status atuais
Antes de trocar para enums, faça um inventário limpo do que você realmente tem em campo. A maioria das equipes acha que tem 6 status e encontra 18 quando olha no banco, payloads de API, rótulos da UI e logs antigos.
Extraia valores de status de todo lugar onde eles podem aparecer: linhas do banco (atuais e históricas), requisições e respostas de API (incluindo webhooks), estado da UI (filtros, badges, regras de botão), jobs em background e integrações (cobrança, e-mail, envio) e logs ou eventos de analytics.
Depois, procure por duplicatas e quase-duplicatas. As óbvias são variantes ortográficas como canceled vs cancelled. As sorrateiras são sinônimos que quase significam o mesmo, como paid vs payment_received, ou valores que misturam estado e causa, como failed vs declined.
Em seguida, escolha nomes canônicos e descreva cada status em linguagem simples. Uma frase serve, mas precisa ser específica. Por exemplo, “paid” pode significar “capturamos o dinheiro” ou “geramos uma fatura”. São workflows diferentes.
Um cheque rápido é responder duas perguntas para cada status:
- Que eventos podem levar a ele?
- Quais ações são permitidas enquanto você está nele?
Se não conseguir responder claramente, provavelmente há dois status escondidos em um rótulo.
Por fim, decida o que fazer com valores legados que você já armazena. Abordagens comuns: mapear valores legados para o conjunto canônico (seguro e flexível), renomear valores in-place durante uma migração (mais simples mas mais arriscado) ou depreciar valores (parar de gravá-los, continuar lendo temporariamente).
Exemplo: você encontra cancelled em pedidos antigos, canceled em novos e void em uma integração. Pode escolher canceled como canônico, mapear cancelled e void para ele e manter um campo separado para o motivo do cancelamento, se precisar.
Projete um enum de status que continue legível
Um enum de status deve cumprir duas funções ao mesmo tempo: prevenir erros no código e ser fácil de ler quando você depura um incidente. Se parecer “esperto demais”, as pessoas vão contornar e você volta ao caos de strings.
Decida onde a fonte da verdade vive
Escolha um lugar que defina os valores de status e trate todo o resto como consumidor. Para muitas equipes, o backend é a opção mais segura porque ele manda na validação e no armazenamento.
Se houver múltiplos serviços, um módulo ou schema compartilhado pode funcionar, mas só se ele for versionado e atualizado de forma confiável.
Uma regra simples ajuda: defina status uma vez, importe-os onde precisar e bloqueie strings ad-hoc em revisão de código. Assim você evita uma segunda lista concorrente de status.
Regras de nomenclatura que ajudam
Statuses devem parecer códigos internos estáveis, não rótulos de UI. Escolha um formato e mantenha-o em todo o app.
Regras chatas funcionam melhor:
- Use um tempo consistente, geralmente passado para estados concluídos (ex.:
PAID,CANCELED,FULFILLED) - Evite sinônimos (
CANCELLEDvsCANCELED). Escolha uma grafia e aplique-a - Mantenha códigos curtos e claros. Se precisar de uma frase para explicar, o estado provavelmente é específico demais
- Reserve
UNKNOWNpara migrações reais, não como esconderijo para bugs
Separe o texto para usuários. O valor do enum é para máquinas e logs. A UI pode mapear CANCELED para “Cancelled by customer” ou “Order cancelled”, dependendo do contexto, idioma e tom.
Para qualquer status que possa ser mal interpretado, adicione um comentário curto onde ele é definido: quando se torna válido e o que deve ser verdadeiro antes de acontecer. Exemplo: “REFUNDED: apenas depois de PAID; nunca definido diretamente a partir de PENDING.” Notas pequenas assim evitam transições acidentais no futuro.
Passo a passo: refatore o código da aplicação
Comece pela camada de aplicação. Você quer que o código pare de aceitar strings aleatórias muito antes de tocar em todas as linhas do banco.
1) Adicione o enum (sem usá-lo em todo lugar ainda)
Crie um tipo enum em um lugar central e faça dele a fonte de verdade. Mantenha nomes consistentes.
// Exemplo (TypeScript)
export enum OrderStatus {
Draft = "DRAFT",
Submitted = "SUBMITTED",
Approved = "APPROVED",
Canceled = "CANCELED",
}
2) Migre comparações e ramificações, depois force exaustividade
A maioria dos bugs de workflow mora em checagens pequenas como if (status === "cancelled"). Substitua isso por comparações com enums para que erros de grafia não compilem.
Uma ordem de refactor que costuma funcionar:
- Substitua comparações de string por valores do enum (
status === OrderStatus.Canceled) - Torne switch statements exaustivos para que estados faltantes falhem alto
- Atualize tipos para que variáveis comuniquem a mudança (
status: OrderStatus, nãostatus: string) - Remova branches de fallback que escondem casos faltantes
- Busque literalmente por literais de status e limpe um por um
Se sua linguagem suportar, use um padrão “assert never” (ou checagens do compilador) para que ao adicionar um status novo você seja forçado a tratá-lo em todos os lugares.
3) Adicione validação nas bordas (onde strings ainda entram)
Mesmo depois do refactor, entradas chegam como strings: requisições HTTP, eventos de webhook, payloads de jobs, mensagens enfileiradas. Valide e converta na borda, e mantenha enums internamente.
Boas validações de borda incluem rejeitar status desconhecidos cedo com um erro claro, isolar valores inesperados em quarentena em vez de chutar uma resposta, validar payloads de jobs antes de alterar estado e restringir dropdowns administrativos à lista do enum.
4) Mantenha um adaptador temporário para strings legadas
Durante o rollout você pode precisar ler valores antigos como cancelled de registros armazenados ou callbacks de terceiros. Adicione um pequeno adaptador que mapeie strings legadas para o enum e mantenha-o isolado.
Esse padrão mantém entradas bagunçadas nas bordas, converte uma vez e torna a lógica central resistente a um único erro de digitação.
Atualize o banco sem surpresas de downtime
Mudanças no banco são onde refactors de status normalmente vão pro lado errado. A abordagem mais segura é adicionar a nova estrutura primeiro, manter leituras antigas funcionando e só apertar regras quando o app estiver escrevendo os novos valores.
Tipo enum vs tabela lookup
Você normalmente tem duas boas opções:
- Tipo enum no banco: rápido, compacto e bloqueia valores inválidos, mas pode ser chato de mudar depois
- Tabela de lookup (statuses + foreign key): fácil de estender e pode armazenar metadata, mas adiciona um join e mais setup
Se espera mudar a lista com frequência (novos estados, estados aposentados, variações por tenant), uma tabela de lookup costuma ser a escolha mais tranquila.
Um padrão de migração seguro
Para mover de strings para enums sem downtime, use um fluxo expand → migrate → contract:
- Expand: adicione uma nova coluna (por exemplo
status_v2) ou o novo tipo enum enquanto mantém a colunastatusantiga. Não adicione constraint rígida ainda. - Dual write: atualize o app para escrever novo e antigo ao mesmo tempo (
statusestatus_v2). Leituras antigas continuam usando o campo antigo, então nada quebra. - Backfill: execute um job que mapeie strings antigas para os novos valores. Seja explícito com dados bagunçados: corte espaços, normalize case e decida o que fazer com desconhecidos (quarentena ou mapear para fallback seguro).
- Lock it down: depois do backfill e com dual write ativo, adicione constraints para impedir dados ruins adiante (constraint de enum, foreign key, possivelmente
NOT NULL). - Contract (cleanup): mude leituras para o novo campo, monitore um ciclo de release completo e então remova a coluna antiga ou mantenha-a temporariamente para compatibilidade com código legado.
Antes de adicionar NOT NULL, verifique quantas linhas ainda faltam status_v2. Se o número não for zero, corrija o mapeamento primeiro para evitar falhas de migração.
Alinhe API, UI e integrações
Depois de mover para enums, o maior ganho vem de fazer todos os pontos de entrada concordarem sobre os mesmos valores permitidos. Se sua API aceitar qualquer coisa, a UI ainda pode enviar erros de grafia, e um webhook pode injetar strings antigas.
Tranque o contrato da API
Atualize o schema da API e a validação de requisição para que apenas statuses conhecidos sejam aceitos. Se alguém enviar um valor desconhecido, falhe cedo com uma mensagem que uma pessoa não técnica consiga entender.
Checagens práticas:
- Valide status em toda escrita (create, update, bulk update), não só em um endpoint
- Retorne um erro claro: "Status deve ser um dos: pending, approved, canceled" (inclua o que foi recebido)
- Faça respostas consistentes: sempre retorne o valor canônico do enum
- Adicione testes que tentem erros comuns (como cancelled) e confirmem que são rejeitados
Mantenha UI e integrações honestas
No frontend, evite strings hard-coded em vários lugares. Dropdowns, filtros e badges devem vir dos mesmos valores permitidos que o backend aplica. Do contrário, alguém “renomeia um rótulo” e muda acidentalmente o valor enviado ao servidor.
Para integrações externas, muitas vezes você não consegue mudar o outro lado rápido. Use versionamento ou uma camada de tradução que aceite valores antigos e os mapeie para o enum. Exemplo: um parceiro pode ainda enviar cancelled enquanto seu enum usa canceled. Aceite temporariamente, mapeie e registre um aviso para saber o que precisa ser atualizado. Defina uma data para remover o mapeamento de compatibilidade.
Também atualize analytics e relatórios para que gráficos não se dividam entre strings antigas e novas. Normalize valores históricos para o enum antes de rodar dashboards ou exports.
Exemplo: o bug canceled vs cancelled em um workflow real
Um lugar comum onde isso pega é um sistema de pedidos onde o status controla três coisas ao mesmo tempo: reembolsos, e-mails ao cliente e fulfillment. Parece simples até um erro de grafia criar um segundo “status válido”.
Imagine um fluxo de checkout que define order.status = "cancelled" (com dois L) quando um comprador cancela. Mas o job de reembolso checa por "canceled" (com um L) porque alguém copiou a grafia de outro arquivo. Agora você tem dois ramos que nunca se encontram.
Como falha na prática:
- A UI mostra “Cancelled”, então suporte assume que o reembolso está em progresso
- O worker de reembolso nunca processa (filtra por
canceled) - O fulfillment pode continuar se só bloquear
canceled, então um pedido “cancelled” pode ser enviado - Templates de e-mail podem divergir, e o cliente recebe a mensagem errada
Com um enum, não há duas grafias. Há um valor, e código que tente usar qualquer outra coisa não vai compilar (ou vai falhar na validação cedo, dependendo do stack).
Dados antigos e eventos antigos ainda precisam de cuidado. Uma abordagem prática é migrar linhas existentes mapeando ambas as strings (canceled, cancelled) para um único valor enum, manter um fallback temporário ao ler eventos legados e adicionar uma auditoria que conte statuses desconhecidos para você corrigir os casos sobressalentes.
Erros comuns e armadilhas durante uma refatoração de status
Refactors de status falham menos porque enums são difíceis e mais porque o mundo antigo e o novo vão coexistir por um tempo. Essa sobreposição é onde bugs se escondem.
Uma armadilha comum é deixar enums no backend enquanto a API, UI ou banco ainda aceita texto livre. Você acaba com uma migração pela metade: parte do código usa OrderStatus.Canceled e outra parte ainda escreve "cancelled". Se precisar suportar ambos durante a mudança, centralize a conversão em um lugar e faça todo o resto usar o enum.
Outro deslize é renomear um status sem perseguir todos os consumidores “invisíveis”. Filtros de dashboard, exports CSV, alertas ou uma visão de suporte podem ainda procurar o valor antigo. O app parece ok até alguém dizer: “o relatório de pedidos cancelados está vazio.”
Jobs em background e integrações de saída são fáceis de esquecer. A UI pode parar de enviar strings, mas um job noturno, handler de webhook ou callback de pagamento pode continuar definindo status direto. Trate qualquer valor de status externo como input não confiável e traduza-o.
Erros que causam mais dor:
- Manter strings e enums ativos em camadas diferentes por semanas, gerando confusão sobre o canônico
- Mudar nomes de status sem atualizar filtros salvos, exports, alertas e dashboards administrativos
- Esquecer escritores não-UI como cron jobs, filas, webhooks e callbacks de terceiros
- Deixar status vazios ou desconhecidos passarem por validação fraca ("default para string vazia" é clássico)
- Pular testes para estados raros como chargeback, revisão manual, expirado ou disputa
Um exemplo real: você adiciona um enum e mapeia "cancelled" para Canceled, mas esquece um caminho antigo que ainda escreve "canceled" como string bruta. Agora você tem duas grafias no banco de novo, e o job de reembolso só pega uma delas.
Para reduzir surpresas antes do deploy, rejeite valores desconhecidos nas bordas (API inputs, payloads de webhook), registre e conte mapeamentos de fallback para ver quem ainda envia strings legadas, rode testes com registros parecidos com produção que incluam grafias antigas e defina uma data depreciação para entradas em string (e então remova de fato).
Checklist rápido e próximos passos práticos
A meta é simples: o app deve aceitar apenas statuses conhecidos, armazenar apenas statuses conhecidos e exibir apenas statuses conhecidos.
Antes de declarar o refactor pronto, verifique:
- Procure no código por strings literais de status. Idealmente, elas aparecem em um só lugar: um pequeno adaptador que traduz entradas legadas (valores antigos no BD, webhooks, clientes antigos) para seu enum.
- Faça o banco rejeitar status inválidos (tipo enum nativo, constraint check ou tabela de referência) e confirme que valores antigos foram migrados.
- Confirme que API e UI concordam sobre as opções permitidas e usam a mesma fonte de verdade.
- Verifique logs e analytics. Se você rastreia mudanças de status, garanta que dashboards não se dividam em dois status semelhantes.
Alguns testes apontuais pagam a conta:
- Tente analisar ou definir um status desconhecido (como
"cancelled"quando o enum só permite"canceled") e espere um erro claro - Rode um teste de fluxo de ponta a ponta (create → pay → cancel) e afirme que o status salvo é exatamente o valor do enum
- Se tiver integrações, envie um status legado ao adaptador e confirme que ele mapeia corretamente (e rejeita valores realmente inválidos)
Se essa bagunça veio de um protótipo gerado por IA, uma auditoria rápida frequentemente revela writes de status espalhados por handlers, rotas de API e workers. FixMyMess (fixmymess.ai) foca em diagnosticar e reparar esse tipo de padrão que quebra produção, e uma refatoração para enums costuma ser uma das formas mais rápidas de impedir falhas silenciosas de workflow antes de um cleanup mais profundo.
Perguntas Frequentes
Por que campos de status em string são tão arriscados em workflows reais?
Strings são fáceis de digitar errado, e o código normalmente não quebra quando isso acontece. Um valor como "cancelled" em vez de "canceled" pode fazer com que o reembolso não seja executado, o fulfillment não pare ou o e-mail correto não seja enviado — tudo isso sem erros óbvios.
Quando devo trocar strings por enums?
Use enums quando um status controlar ramificações importantes, jobs, faturamento, acesso, fulfillment ou mensagens. Se um erro de digitação pode levar a um “caminho errado” silencioso em vez de um erro claro, você terá muito ganho com a refatoração para enums.
Enums vão consertar automaticamente minha lógica de negócio quebrada?
Não. Enums impedem valores inválidos e tornam mais fácil detectar casos faltantes, mas não resolvem regras de negócio confusas ou partes do sistema que escrevem status de forma conflituosa. Ainda é preciso definir claramente o significado de cada status e quem é responsável pelas transições.
Como lidar com rótulos de exibição sem misturá-los ao status armazenado?
Mantenha os valores armazenados estáveis e sem frescura, por exemplo CANCELED, e mapeie-os para rótulos na UI como “Canceled” ou “Cancelled”. Alterar o texto exibido ao usuário não deve exigir mudar valores no banco ou contratos de API.
Qual a forma mais rápida de encontrar todos os valores de status que meu app realmente usa?
Extraia todos os valores distintos do banco de dados, payloads de API, lógica da UI, jobs e logs, depois normalize e agrupe quase-duplicatas. Você quase sempre encontrará mais variantes do que imaginava, especialmente em código gerado por IA.
Como devo lidar com valores legados como "cancelled" ou sinônimos estranhos?
Escolha um valor canônico e mapeie todas as grafias e sinônimos legados para ele em um único adaptador. Mantenha o adaptador na borda, registre cada mapeamento de fallback e estabeleça uma data para remover a compatibilidade depois que os remetentes forem atualizados.
Qual a ordem de operação segura para refatorar o código para enums?
Primeiro substitua as comparações, depois endureça os tipos para que status não seja mais uma string livre na lógica principal. Adicione validação nas bordas onde as strings entram e só então faça a migração do banco, assim você evita reintroduzir erros por strings antigas.
Como posso migrar o banco sem downtime ou falhas-surpresa?
Use o fluxo expand → dual write → backfill → lock down → contract. Adicione a nova coluna ou tipo primeiro, escreva ambos por um tempo, faça o backfill das linhas existentes, adicione constraints e então alterne as leituras para o novo campo.
Como impedir que status inválidos entrem por APIs, webhooks ou jobs?
Valide e converta na borda, não dentro da lógica do workflow. Rejeite status desconhecidos com um erro claro e, para entradas legadas inevitáveis, traduza-as uma vez em uma camada dedicada antes de qualquer mudança de estado.
Herdei código gerado por IA com status bagunçados — qual a forma mais rápida de estabilizá-lo?
Protótipos gerados por IA costumam espalhar writes de status por handlers de UI, rotas de API e workers, o que torna erros de grafia e grafias divergentes muito comuns. Se quiser um diagnóstico rápido e uma refatoração segura para enums (além de correções como auth, secrets e lógica de workflow), FixMyMess pode começar com uma auditoria gratuita de código e normalmente reverter projetos em 48–72 horas.