13 sept 2025·8 min de lectura

Sustituye campos de estado en string por enums para evitar errores tipográficos en workflows

Sustituye campos de estado en cadena por enums y evita que pequeños errores tipográficos —como canceled vs cancelled— rompan flujos de pedido, aprobación o pago.

Sustituye campos de estado en string por enums para evitar errores tipográficos en workflows

Por qué los estados en cadena rompen flujos de trabajo tan fácilmente

Los campos de estado en string parecen inofensivos porque se leen bien en logs y bases de datos. El problema es que son texto plano, así que el código no te protege de pequeñas diferencias como canceled vs cancelled, Paid vs paid o incluso un espacio al final.

Un solo typo basta para saltarse una rama crítica. Imagina un flujo de checkout que debe reembolsar un pago y detener el fulfillment cuando una orden se cancela:

if (order.status === "canceled") {
  refund(order.paymentId)
  stopFulfillment(order.id)
  sendCancelEmail(order.customerEmail)
}

Si una parte de la app escribe cancelled, esa condición nunca se ejecuta. No hay un crash. La orden simplemente deriva hacia el camino equivocado, y te das cuenta más tarde cuando se cobra a un cliente, el almacén envía el pedido o no se envía un email.

Estos bugs pasan las revisiones por una razón simple: las cadenas no muestran un “conjunto permitido” claro. Los revisores ven una comprobación de estado y asumen que el valor es válido. Incluso si alguien nota la ortografía, no queda claro si el resto del sistema usa la variante americana o británica.

Las pruebas también suelen fallar en detectarlo. Los desarrolladores tienden a copiar la misma cadena en la prueba y en el código, así que el test pasa aunque los datos reales sean inconsistentes.

El daño suele aparecer donde los flujos tienen consecuencias reales:

  • Pagos: no se disparan reembolsos, se reintenta cuando debería detenerse
  • Aprobaciones: solicitudes que quedan atascadas en “pending” para siempre o se aprueban por error
  • Emails y notificaciones: se envía el mensaje equivocado o no se envía nada
  • Fulfillment y acceso: envíos que continúan, suscripciones activas, cuentas que no se bloquean

Al sustituir campos de estado en string por enums, cambias quién es responsable de la corrección. En lugar de que cada desarrollador recuerde la escritura exacta, el compilador (o al menos tu verificador de tipos) aplica una lista única de estados válidos.

Este refactor no arreglará por sí solo lógica de negocio mala, condiciones de carrera o falta de trazabilidad. Simplemente hace más difícil escribir “estados imposibles”, de modo que el resto del código de workflow sea más confiable.

Si heredaste código generado por IA, las cadenas de estado son uno de los puntos de fallo silenciosos más comunes. Es normal encontrar typos repartidos por UI, manejadores de API y jobs en segundo plano.

Qué arreglan los enums (y qué no)

Un enum es una lista con nombre de valores permitidos. En lugar de guardar una string libre como pending o cancelled, eliges de un conjunto fijo como PENDING, PAID, CANCELED. El código trata esos valores como las únicas opciones válidas.

La mayor ganancia es tener una única fuente de verdad sobre qué estados existen. No tienes que recordar si es cancelled, canceled, CANCELLED u order_cancelled. El enum hace explícito el conjunto permitido y todo lo demás es inválido.

Los enums también te ayudan a fallar pronto. Con strings, un typo puede llegar a producción y romper un flujo solo en una ruta rara. Con enums, muchos errores se detectan antes:

  • Tu editor y compilador pueden autocompletar y marcar valores desconocidos
  • Los tests unitarios fallan en el punto exacto donde se asigna un estado inválido
  • Los switch y match pueden advertirte cuando olvidaste manejar un estado nuevo
  • Los esquemas de API y validadores pueden rechazar entradas malas de inmediato

Lo que los enums no hacen es resolver reglas de negocio poco claras. Si el equipo no se pone de acuerdo sobre cuándo una orden debe ser ON_HOLD frente a PENDING, un enum no lo va a resolver. Tampoco arregla automáticamente un ciclo de vida desordenado donde varias partes de la app escriben el estado de forma conflictiva. Los enums hacen esos problemas más visibles, pero aún necesitas reglas claras y un responsable.

Valores almacenados vs etiquetas para mostrar

Un error común es mezclar lo que se guarda con lo que se muestra.

Los valores almacenados deben ser estables y aburridos, porque acaban en bases de datos, logs e integraciones. Las etiquetas para mostrar pueden ser amigables y cambiar sin romper nada.

Por ejemplo, guarda CANCELED como valor del enum, pero muestra “Canceled” en la UI. Si más adelante quieres mostrar “Cancelled” para usuarios en UK, eso debe ser un cambio de etiqueta de UI, no una migración de base de datos.

Esta distinción importa aún más cuando limpias código generado por IA, donde los prototipos a menudo hardcodean cadenas de UI como valores de estado. Separar enums internos del texto para humanos ayuda a evitar que el próximo typo se convierta en un incidente en producción.

Empieza mapeando tus estados actuales

Antes de cambiar a enums, haz un inventario limpio de lo que realmente tienes en el terreno. La mayoría de equipos cree tener 6 estados y luego encuentra 18 cuando revisa la base de datos, payloads de API, etiquetas de UI y logs antiguos.

Extrae valores de estado de todos los lugares donde puedan aparecer: filas de base de datos (actuales e históricas), peticiones y respuestas de API (incluidos webhooks), estado de UI (filtros, badges, reglas de botones), jobs e integraciones (facturación, email, envío) y eventos en logs o analytics.

Luego busca duplicados y casi duplicados. Los obvios son variantes de ortografía como canceled vs cancelled. Los escurridizos son sinónimos que casi significan lo mismo, como paid vs payment_received, o valores que mezclan estado y causa como failed vs declined.

Después, elige nombres canónicos y escribe qué significa cada estado en lenguaje llano. Una frase basta, pero debe ser específica. Por ejemplo, “paid” puede significar “hemos capturado dinero” o “hemos generado una factura”. Esas implican workflows distintos.

Una comprobación rápida es responder dos preguntas por cada estado:

  • ¿Qué eventos pueden llevar a él?
  • ¿Qué acciones están permitidas mientras estás en él?

Si no puedes responder claramente, probablemente tienes dos estados dentro de una misma etiqueta.

Finalmente, decide qué hacer con valores heredados que ya almacenas. Enfoques comunes: mapear valores heredados al conjunto canónico (seguro y flexible), renombrar valores en sitio durante una migración (más simple pero más arriesgado), o deprecar valores (dejar de escribirlos y seguir leyéndolos temporalmente).

Ejemplo: encuentras cancelled en órdenes antiguas, canceled en nuevas y void en una integración. Podrías elegir canceled como canónico, mapear cancelled y void a él y mantener un campo separado para la razón de cancelación si lo necesitas.

Diseña un enum de estados que siga siendo legible

Un enum debe hacer dos cosas a la vez: prevenir errores en el código y seguir siendo fácil de leer cuando debuggeas un incidente. Si parece “ingenioso”, la gente lo evitará y volverás al caos de strings.

Decide dónde vive la fuente de verdad

Elige un lugar que defina los valores de estado y trata todo lo demás como consumidor. Para muchos equipos, el backend es la fuente de verdad más segura porque controla validación y almacenamiento.

Si tienes varios servicios, un módulo compartido o un schema puede funcionar, pero solo si lo versionas y mantienes actualizado.

Una regla simple ayuda: define los estados una vez, impórtalos en todas partes y bloquea strings ad-hoc en la revisión de código. Así evitas crear una segunda lista en competencia.

Reglas de nombrado que te mantienen cuerdo

Los estados deben parecer códigos internos estables, no etiquetas de UI. Elige un formato y síguelo en toda la app.

Reglas aburridas que funcionan mejor:

  • Usa un tiempo consistente, usualmente pasado para estados completados (por ejemplo, PAID, CANCELED, FULFILLED)
  • Evita sinónimos (CANCELLED vs CANCELED). Elige una ortografía y aplícala
  • Mantén los códigos cortos y claros. Si necesitas una frase para explicarlo, el estado probablemente es demasiado específico
  • Reserva UNKNOWN para necesidades reales de migración, no como escondite para bugs

Separa el texto de usuario. El valor del enum es para máquinas y logs. La UI debe mapear CANCELED a “Cancelled by customer” o “Order cancelled” según contexto, idioma y tono.

Para cualquier estado que pueda causar confusión, añade un comentario corto donde se define: cuándo se vuelve válido y qué debe ser cierto antes de que pueda ocurrir. Ejemplo: “REFUNDED: solo después de PAID; nunca establecer directamente desde PENDING.” Pequeñas notas así evitan transiciones accidentales después.

Paso a paso: refactoriza el código de la aplicación

Detén fallos silenciosos en workflows
Arregla fallos silenciosos en flujos de trabajo de apps generadas por IA antes de que afecten pagos, emails o fulfillment.

Empieza en la capa de aplicación primero. Quieres que el código deje de aceptar strings aleatorias mucho antes de tocar cada fila de la base de datos.

1) Añade el enum (sin usarlo en todas partes todavía)

Crea un tipo enum en un lugar central y hazlo fuente de verdad. Mantén los nombres consistentes.

// Example (TypeScript)
export enum OrderStatus {
  Draft = "DRAFT",
  Submitted = "SUBMITTED",
  Approved = "APPROVED",
  Canceled = "CANCELED",
}

2) Migra comparaciones y branching, luego fuerza exhaustividad

La mayoría de bugs de workflow viven en comprobaciones pequeñas como if (status === "cancelled"). Sustitúyelas por comparaciones con el enum para que los typos no compilen.

Un orden de refactor que suele funcionar:

  • Reemplaza comparaciones de strings por valores del enum (status === OrderStatus.Canceled)
  • Haz que los switch sean exhaustivos para que estados faltantes fallen de forma notoria
  • Actualiza los tipos para que las variables comuniquen el cambio (status: OrderStatus, no status: string)
  • Elimina ramas de “fallback a default” que ocultan casos faltantes
  • Busca literales de estado y arréglalos uno a uno

Si tu lenguaje lo soporta, usa un patrón de “assert never” (o chequeos del compilador) para que añadir un estado nuevo te obligue a manejarlo en todas partes.

3) Añade validación en los límites (donde las strings aún entran)

Aunque refactorices, las entradas siguen llegando como strings: requests HTTP, eventos de webhooks, payloads de jobs, mensajes en colas. Valida y convierte en el boundary y luego mantén enums adentro.

Buenas comprobaciones en los límites incluyen rechazar estados desconocidos con un error claro, poner en cuarentena valores inesperados en eventos en lugar de adivinar, validar payloads de jobs antes de cambiar estado y restringir dropdowns de admin a la lista del enum.

4) Mantén un adaptador temporal para strings heredadas

Durante el despliegue quizá necesites leer valores antiguos como cancelled de registros almacenados o callbacks de terceros. Añade un pequeño adaptador que mapee strings heredadas al enum y mantenlo aislado.

Este patrón mantiene la suciedad en los bordes, convierte una sola vez y hace que la lógica central sea difícil de romper con un typo.

Actualiza la base de datos sin sorpresas de downtime

Los cambios en la base de datos son donde los refactors de estado a menudo salen mal. El enfoque más seguro es añadir estructura nueva primero, mantener las lecturas antiguas funcionando y solo endurecer reglas cuando la app escriba plenamente los nuevos valores.

Tipo enum vs tabla de lookup

Típicamente tienes dos buenas opciones:

  • Tipo enum en la base: rápido, compacto y bloquea valores inválidos, pero puede ser molesto de cambiar después
  • Tabla lookup (tabla statuses + FK): fácil de extender y puede almacenar metadata, pero añade un join y más configuración

Si esperas que la lista cambie con frecuencia (nuevos estados, retiros, variaciones por tenant), una tabla lookup suele ser la opción más flexible.

Un patrón de migración seguro

Para pasar de strings a enums sin downtime, usa un flujo expand → migrate → contract:

  1. Expand: añade una columna nueva (por ejemplo status_v2) o el nuevo tipo enum y deja la columna vieja status sin tocar. No añadas la restricción aún.
  2. Dual write: actualiza la app para que nuevas o actualizadas escriban ambos campos (status y status_v2). Las lecturas siguen usando el campo viejo, así no se rompe nada.
  3. Backfill: ejecuta un job único que mapee las strings antiguas a los nuevos valores. Sé explícito con datos sucios: trim, normaliza mayúsculas/minúsculas y decide qué hacer con desconocidos (poner en cuarentena o mapear a un fallback seguro).
  4. Lock it down: cuando el backfill y la escritura dual estén completos, añade constraints para impedir malos datos en el futuro (constraint de enum, FK y posiblemente NOT NULL).
  5. Contract (limpieza): cambia las lecturas al nuevo campo, monitoriza un ciclo de release completo y luego elimina la columna vieja o mantenla temporalmente por compatibilidad.

Antes de añadir NOT NULL, comprueba cuántas filas aún faltan en status_v2. Si la cuenta no es cero, arregla el mapeo primero. Eso evita fallos sorpresa en la migración.

Alinea la API, UI e integraciones

Encuentra escrituras de estado dispersas
Localizaremos escritores de estado en conflicto en UI, rutas de API y jobs en background.

Cuando ya estés en enums, la mayor ganancia viene de hacer que todos los puntos de entrada se pongan de acuerdo en los mismos valores permitidos. Si tu API acepta cualquier cosa, la UI todavía puede enviar typos y un webhook puede inyectar strings antiguas.

Endurece el contrato de la API

Actualiza el schema de la API y la validación de requests para que solo se acepten estados conocidos. Si alguien envía un valor desconocido, falla pronto con un mensaje que una persona no técnica pueda entender.

Comprobaciones prácticas:

  • Valida el estado en toda operación de escritura (create, update, bulk update), no solo en un endpoint
  • Devuelve un error claro como: "Status must be one of: pending, approved, canceled" (y muestra qué se recibió)
  • Haz respuestas consistentes: siempre devuelve el valor canónico del enum
  • Añade tests que prueben typos comunes (como cancelled) y confirma que los rechazas

Mantén honesta la UI y las integraciones

En frontend, evita strings hardcodeadas en muchos lugares. Dropdowns, filtros y badges deben venir de los mismos valores permitidos que el backend hace cumplir. Si no, alguien “renombra una etiqueta” y accidentalmente cambia el valor enviado al servidor.

Con integraciones externas, a menudo no puedes cambiar el otro lado rápido. Usa versionado o una capa de traducción que acepte valores viejos y los mapee al enum. Ejemplo: un partner puede seguir enviando cancelled mientras tu enum usa canceled. Acepta temporalmente, mapea y registra una advertencia para saber quién queda por actualizar. Fija una fecha para eliminar la compatibilidad.

También actualiza analytics y reporting para que los gráficos no se dividan entre strings viejas y nuevas. Normaliza valores históricos al enum antes de ejecutar dashboards o exportaciones.

Ejemplo: el bug canceled vs cancelled en un flujo real

Un sitio común donde esto duele es en un sistema de órdenes donde el estado controla tres cosas a la vez: reembolsos, emails al cliente y fulfillment. Parece simple hasta que un error de ortografía crea un segundo “estado válido”.

Imagina un flujo de checkout que pone order.status = "cancelled" (doble L) cuando un comprador cancela. Pero el worker de reembolsos comprueba "canceled" (una sola L) porque alguien copió la ortografía de otro archivo. Ahora tienes dos ramas que nunca se encuentran.

Cómo falla en la vida real:

  • La UI muestra “Cancelled”, así que soporte asume que el reembolso está en proceso
  • El worker de reembolsos nunca lo procesa (filtra por canceled)
  • El fulfillment puede seguir si solo bloquea canceled, así que una orden “cancelled” puede enviarse
  • Las plantillas de email se fragmentan también, y el cliente recibe el mensaje equivocado

Con un enum no tienes dos ortografías. Tienes un solo valor y el código que intente usar cualquier otra cosa no compilará (o fallará la validación temprano, según tu stack).

Los datos y eventos antiguos necesitan cuidado. Un enfoque práctico es migrar filas existentes mapeando ambas strings (canceled, cancelled) al mismo valor de enum, mantener un fallback temporal al leer eventos heredados y añadir una auditoría pequeña que cuente estados desconocidos para corregir rezagados.

Errores comunes y trampas durante un refactor de estado

Convierte un prototipo en producción
Diagnosticamos y reparamos prototipos construidos por IA para llevarlos a producción.

Los refactors fallan menos porque los enums sean difíciles y más porque el mundo viejo y nuevo coexiste durante un tiempo. Esa superposición es donde se esconden los bugs.

Una trampa común es dejar enums en el backend mientras la API, UI o base de datos siguen aceptando texto libre. Terminas con una migración a medias donde parte del código usa OrderStatus.Canceled y otra parte sigue escribiendo "cancelled". Si debes soportar ambos durante el cambio, centraliza la conversión en un solo lugar y haz que todo lo demás use el enum.

Otro fallo frecuente es renombrar un estado sin perseguir a todos los consumidores “invisibles”. Filtros de dashboard, exportes CSV, alertas o vistas de soporte pueden seguir buscando el valor antiguo. La app parece bien hasta que alguien dice: “el informe de órdenes canceladas está vacío”.

Jobs en background y integraciones salientes son fáciles de olvidar. La UI puede dejar de enviar strings, pero un job nocturno, un handler de webhook o un callback de pagos pueden seguir estableciendo estado directamente. Trata cualquier valor de estado externo como entrada no confiable y tradúcelo.

Errores que causan más dolor:

  • Mantener strings y enums activos en capas diferentes durante semanas, así nadie sabe qué es canónico
  • Cambiar nombres de estado sin actualizar filtros guardados, exportes, alertas y dashboards de admin
  • Olvidar escritores no-UI como cron jobs, colas, webhooks y callbacks de terceros
  • Dejar pasar estados vacíos o desconocidos por validación débil (“default a string vacía” es clásico)
  • Saltarse tests para estados raros como chargeback, revisión manual, expirado o disputa

Un ejemplo pequeño: añades un enum y mapeas "cancelled" a Canceled, pero olvidas un camino antiguo que aún escribe "canceled" como string cruda. Ahora tienes dos ortografías en la base de datos otra vez y el job de reembolso solo recoge una de ellas.

Para reducir sorpresas antes del deploy, rechaza valores desconocidos en los límites (API inputs, handlers de webhooks), registra y cuenta los mapeos de fallback para ver quién sigue enviando strings heredadas, ejecuta tests contra registros similares a producción que incluyan ortografías antiguas y fija una fecha de deprecación para inputs en string (y luego elimínala realmente).

Checklist rápido y pasos prácticos siguientes

El objetivo es simple: la app debe aceptar solo estados conocidos, almacenar solo estados conocidos y mostrar solo estados conocidos.

Antes de dar por terminado el refactor, verifica estos puntos:

  • Busca en la base de código literales de estado. Idealmente solo los encuentres en un lugar: un pequeño adaptador que traduzca entradas heredadas (DB antigua, webhooks entrantes, clientes antiguos) al enum.
  • Haz que la base de datos rechace estados inválidos (tipo enum nativo, check constraint o tabla de referencia) y confirma que los valores antiguos fueron migrados.
  • Confirma que API y UI están de acuerdo sobre las opciones permitidas y usan la misma fuente de verdad.
  • Revisa logs y analytics. Si rastreas cambios de estado, asegúrate de que los dashboards no se dividan silenciosamente entre dos estados similares.

Unos tests dirigidos suelen pagarse solos:

  • Intenta parsear o asignar un estado desconocido (como "cancelled" cuando el enum solo permite "canceled") y espera un error claro
  • Ejecuta un test de workflow end-to-end (create → pay → cancel) y afirma que el estado almacenado es exactamente el valor del enum
  • Si tienes integraciones, alimenta el adaptador con un estado heredado y confirma que lo mapea correctamente (y que rechaza valores realmente inválidos)

Si este desorden vino de un prototipo generado por IA, una auditoría rápida suele revelar escrituras de estado repartidas por handlers, rutas de API y jobs. FixMyMess (fixmymess.ai) se centra en diagnosticar y reparar estos patrones que rompen producción, y un refactor a enums suele ser una de las formas más rápidas de detener fallos silenciosos en workflows antes de una limpieza más profunda.

Preguntas Frecuentes

¿Por qué los campos de estado en string son tan riesgosos en flujos reales?

Las cadenas son fáciles de escribir mal y el código normalmente no falla cuando pasa. Un valor como "cancelled" en lugar de "canceled" puede hacer que no se procese un reembolso, que no se detenga el fulfillment o que no se envíe el email correcto, sin que nada se rompa visiblemente.

¿Cuándo debería cambiar de strings a enums?

Usa enums cuando un estado controla ramas importantes, jobs, facturación, acceso, fulfillment o mensajería. Si un typo puede provocar un “camino equivocado” silencioso en vez de un error claro, ganarás mucho con un refactor a enums.

¿Los enums arreglarán automáticamente mi lógica de negocio rota?

No. Los enums previenen valores inválidos y hacen que falten casos sean más fáciles de detectar, pero no arreglan reglas de negocio confusas ni escritores en conflicto. Aún necesitas definir el significado de cada estado y qué transiciones están permitidas.

¿Cómo manejo etiquetas para mostrar sin mezclarlas con el estado almacenado?

Mantén los valores almacenados estables y sencillos, por ejemplo CANCELED, y mapea eso a etiquetas amigables en la UI como “Canceled” o “Cancelled”. Cambiar el texto para usuarios no debería requerir migrar la base de datos ni el contrato de la API.

¿Cuál es la forma más rápida de encontrar todos los valores de estado que mi app realmente usa?

Extrae cada valor distinto de la base de datos, cargas de API, lógica de UI, jobs y logs, luego normaliza y agrupa las variantes. Suele aparecer muchas más variantes de las que esperas, sobre todo en código generado por IA.

¿Cómo trato valores heredados como "cancelled" o sinónimos raros?

Elige un valor canónico y mapea todas las ortografías y sinónimos heredados a él en un único adaptador. Mantén ese adaptador en el límite (boundary), registra cada mapeo de compatibilidad y fija una fecha para eliminarlo una vez que los emisores se actualicen.

¿Cuál es un orden seguro de operaciones para refactorizar a enums?

Primero sustituye las comparaciones, luego ajusta los tipos para que status no pueda ser una string libre en la lógica central. Añade validación en los límites donde las strings entran y, solo entonces, haz la migración de la base de datos para no reintroducir typos continuamente.

¿Cómo puedo migrar la base de datos sin downtime ni fallos inesperados?

Usa un flujo expand → dual write → backfill → lock down → contract. Añade la nueva columna o tipo primero, escribe ambos campos durante un tiempo, backfill de filas existentes, añade las restricciones y finalmente cambia las lecturas y elimina el campo antiguo.

¿Cómo evito que entren estados malos por APIs, webhooks o jobs?

Valida y convierte en el límite (APIs, webhooks, payloads de jobs), no dentro de la lógica de workflow. Rechaza estados desconocidos con un error claro y, para entradas heredadas inevitables, tradúcelas en una capa dedicada antes de cualquier cambio de estado.

Herité código generado por IA con estados desordenados — ¿cuál es la forma más rápida de estabilizarlo?

Los prototipos generados por IA suelen repartir escrituras de estado en handlers de UI, rutas de API y workers, lo que causa typos y ortografías mezcladas. Si quieres un diagnóstico rápido y un refactor a enums seguro para producción (más arreglos como auth, secretos y lógica de workflow), FixMyMess (fixmymess.ai) puede empezar con una auditoría gratuita y normalmente entrega resultados en 48–72 horas.