¿Por qué una integración "funciona" en la demo y falla silenciosamente en producción?

Durante una prueba de aceptación estándar, se envía un mensaje, se verifica su llegada y se confirma. En producción, esa misma integración procesa miles de mensajes al día, y algunos fallarán, ya sea por un tiempo de espera (timeout), un bloqueo de fila, una autorización caducada o un cambio de esquema en el otro sistema. La pregunta que determina la calidad de la solución no es "¿funciona la integración?", sino "¿qué sucede cuando no funciona y quién se da cuenta?".

La mayoría de los fallos costosos que he presenciado no fueron causados por un error en el código de integración en sí, sino por la ausencia de tres capacidades: la detección de que un mensaje ha fallado, un mecanismo que reintenta sin crear duplicados y un proceso que garantiza que la información en ambos sistemas sea coherente al final del día. Sin ellas, cualquier integración "funciona" hasta el momento en que se descubre que lleva dos semanas sin funcionar.

Las cuatro capas que componen una gestión de errores adecuada

CapaQué resuelveFallo típico sin ella
IdempotencyLa reejecución del mismo mensaje no crea un registro duplicadoUn pedido duplicado o un movimiento de inventario duplicado después de un reintento
Retry con BackoffUn fallo temporal (Timeout, Rate Limit) se corrige automáticamenteUna carga momentánea se convierte en un fallo persistente
Dead Letter QueueUn fallo no temporal se marca y no desaparece silenciosamenteUn mensaje se "traga" y las partes creen que fue procesado
Reconciliación de negocioSe detectan discrepancias de datos que no lograron fallar de manera explícitaUn informe mensual revela una discrepancia cuyo origen es difícil de rastrear

Cada capa depende de la anterior. Un reintento sin Idempotencia crea duplicados; una Dead Letter sin Reconciliación oculta el hecho de que incluso los mensajes "técnicamente" exitosos no siempre reflejaron el estado comercial correcto.

Idempotency: La clave para prevenir la duplicidad

Cualquier integración que pueda recibir el mismo mensaje más de una vez —y casi todas las integraciones son así— necesita una clave única externa (External ID) que identifique el evento, no solo el registro. En Salesforce, la implementación común es un Upsert mediante un campo External ID con restricción única, combinado con una tabla de registro (Custom Object o Platform Event Log) que registra qué identificadores de evento ya han sido procesados por completo.

El error común: conformarse con un Upsert en el propio registro de negocio (por ejemplo, Order External ID) sin documentar los pasos intermedios. Si el proceso también incluye una actualización de inventario en un sistema externo, un Upsert en el pedido no evita una llamada duplicada para esa actualización de inventario; cada suboperación con un efecto secundario externo (Side Effect) debe ser idempotente por sí misma, no solo el registro final.

Retry: Políticas de Backoff y clasificación de errores

No todos los errores merecen un reintento. Es fundamental distinguir de antemano entre tres categorías:

  • Errores temporales (Timeout, 503, Rate Limit): Candidatos para reintento con Exponential Backoff, es decir, el intervalo entre intentos aumenta (por ejemplo, 30 segundos, 2 minutos, 10 minutos) para no agravar la carga.
  • Errores estructurales (campo obligatorio ausente, violación de Validation Rule, valor no válido): No se reintentarán, ya que fallarán de la misma manera. Deben pasar directamente a la Dead Letter.
  • Errores de autorización o configuración (token caducado, cambio de API Version): Requieren una notificación inmediata al equipo técnico, ya que bloquean toda la cola y no solo un mensaje individual.

En Salesforce, la implementación del reintento se realiza generalmente en la capa de Middleware o en Apex Queueable/Batch con un contador de intentos almacenado en el propio registro. Un número razonable de intentos para la mayoría de los casos es de 3 a 5 con Backoff, no un reintento infinito; un reintento sin límite convierte un fallo temporal en una carga persistente en ambos sistemas.

Dead Letter Queue: Donde "viven" los mensajes fallidos

Una Dead Letter no es solo un lugar de almacenamiento, es un contrato. Cada mensaje que llega a ella debe contener: el identificador original del evento, el Payload completo, la causa clasificada del fallo, el número de intentos realizados y la hora de entrada en la cola. Sin esta información, la "gestión" de la Dead Letter se convierte en una adivinanza.

Dos enfoques comunes para la implementación en Salesforce:

  1. Un Custom Object dedicado (Integration_Failed_Message__c) con campos estructurados y una List View por tipo de error: Adecuado cuando se necesita transparencia para un equipo de negocio dentro del propio Salesforce.
  2. Una cola externa en la capa de Middleware (por ejemplo, Dead Letter Exchange en MuleSoft/Boomi): Adecuado cuando el equipo técnico monitorea fuera de Salesforce y desea evitar la carga en la Org.

La elección depende de quién debe actuar sobre el fallo: si es el propietario de un proceso de negocio, debe verlo dentro de Salesforce; si es un equipo de integración técnico, es preferible en la capa externa.

Reconciliación de negocio: La verificación que detecta lo que Retry no capturó

Incluso con Idempotency y Retry perfectos, existen fallos que "tienen éxito" desde el punto de vista técnico pero crean una brecha de negocio, como un mensaje recibido y procesado, pero con un valor incorrecto obtenido de una fuente de datos desactualizada. La reconciliación es un proceso periódico (diario, por hora, según la frecuencia de los eventos) que compara un recuento o un monto acumulado entre dos sistemas (por ejemplo, el número de pedidos creados en el ERP frente al número de pedidos creados en Salesforce para el mismo día) y destaca las diferencias antes de que se conviertan en un problema de servicio al cliente.

Un buen proceso de reconciliación no requiere una revisión campo por campo de cada registro; basta con un checksum o un recuento acumulado que indique cuándo es necesario profundizar. En la mayoría de las organizaciones, una frecuencia diaria es suficiente; en procesos financieros o críticos (pedidos, facturación), se requiere una verificación en cuestión de horas.

Marco de decisión: Cuándo cada capa es obligatoria y cuándo se puede prescindir de ella

CriterioIdempotency ObligatoriaRetry Automático ObligatorioDead Letter Separada ObligatoriaReconciliación Diaria Obligatoria
El evento genera un movimiento financiero o de inventario
El evento es unidireccional, solo lectura (Read)No críticoNoNo
Volumen superior a 500 mensajes al díaRecomendado
Socio externo sin SLA de alta disponibilidadSí, con Backoff largoRecomendado
Integración entre dos objetos no financieros de bajo volumenRecomendadoRecomendadoNo necesarioNo

La regla que guía la tabla: Cuanto más significativa sea la implicación financiera o irreversible (envío, facturación, actualización de inventario) de un fallo, las cuatro capas pasan de "deseable" a "obligatorio", independientemente del volumen.

Escenario de ejemplo: Minorista con sincronización de pedidos bidireccional

Una empresa minorista con 40 sucursales utiliza Salesforce para la gestión de pedidos B2B y un ERP externo para el inventario y la facturación. La integración se construyó originalmente con una simple llamada REST: cuando se crea un pedido en Salesforce, una llamada síncrona lo crea en el ERP. Sin Retry, sin Dead Letter.

Durante un período de alta demanda (Black Friday), el ERP comenzó a devolver Timeouts en aproximadamente el 3% de las llamadas. Sin un mecanismo de Retry, ese 3% simplemente "desapareció"; el pedido permaneció en Salesforce con el estado "enviado" sin que el ERP tuviera conocimiento de ello. En dos días, se acumularon alrededor de 140 pedidos que no llegaron al proceso de empaque y se descubrieron solo cuando los clientes llamaron para preguntar dónde estaba la mercancía.

La solución implementada a raíz de esto: una capa Queueable en Apex que reintenta hasta 5 veces con un Backoff de 1/5/15/30/60 minutos; un campo ERP_Sync_Status__c con valores Pending/Synced/Failed; un Custom Object Integration_Failed_Message__c que centraliza los fallos definitivos con un botón "Reintentar" para el equipo de operaciones; y un informe diario de Reconciliación que compara el recuento de pedidos entre los sistemas y envía una alerta de Slack cuando la diferencia supera cero. El tiempo de detección de un fallo similar se redujo de dos días a menos de una hora.

Riesgos comunes y acciones de prevención

RiesgoCómo se manifiesta en la prácticaAcción preventiva
Reintento infinito en un error estructuralEl mismo mensaje falla repetidamente y genera cargaClasificar errores de antemano y enviar errores estructurales directamente a la Dead Letter
Ausencia de clave única para el eventoUn reintento o una llamada duplicada crean un registro duplicadoExternal ID en el evento, no solo en el registro final
Dead Letter sin propietarioLos mensajes se acumulan y nadie los gestionaDefinir un propietario y un SLA de gestión por tipo de evento, no por sistema
Monitoreo únicamente técnico (estado de API)La integración está "en verde" pero la información de negocio no es coherenteAñadir reconciliación que compare el resultado de negocio, no solo el código de respuesta
Backoff constante y demasiado cortoLos reintentos repetidos agravan la carga durante un fallo generalizadoExponential Backoff con un límite definido de intentos

Checklist antes de aprobar un diseño de gestión de errores

  • ☐ Cada evento tiene una clave única (External ID) que previene la duplicidad en la reejecución.
  • ☐ Los errores se clasifican previamente en temporales/estructurales/de autorización, con un tratamiento diferente para cada tipo.
  • ☐ Existe una política de Backoff definida con un número máximo de intentos.
  • ☐ Hay una Dead Letter accesible con Payload completo y motivo del fallo.
  • ☐ Hay un propietario y un SLA de gestión definidos para cada tipo de fallo.
  • ☐ Existe un proceso de reconciliación periódico que compara el resultado de negocio entre los sistemas.
  • ☐ Las alertas llegan a un canal donde alguien las lee activamente (no solo a un registro).
  • ☐ El escenario de prueba incluye la interrupción del servicio del otro sistema, no solo el "Happy Path".

Cómo se conecta esto con el resto de la arquitectura

El diseño de la gestión de errores no es una característica que se añade al final; es la diferencia entre un sistema que se revela roto después de que un cliente se queja y un sistema que se autoadvierte antes de que el daño se acumule. Las cuatro capas —Idempotency, Retry clasificado, Dead Letter con Propietario y Reconciliación de negocio— no requieren un proyecto separado, pero sí una decisión explícita en la fase de planificación, antes de que la primera integración pase a producción. Una organización que se autoalerta sobre un 3% de mensajes fallidos en una hora es fundamentalmente diferente de una organización que lo descubre a través de un cliente insatisfecho.