¿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
| Capa | Qué resuelve | Fallo típico sin ella |
|---|---|---|
| Idempotency | La reejecución del mismo mensaje no crea un registro duplicado | Un pedido duplicado o un movimiento de inventario duplicado después de un reintento |
| Retry con Backoff | Un fallo temporal (Timeout, Rate Limit) se corrige automáticamente | Una carga momentánea se convierte en un fallo persistente |
| Dead Letter Queue | Un fallo no temporal se marca y no desaparece silenciosamente | Un mensaje se "traga" y las partes creen que fue procesado |
| Reconciliación de negocio | Se detectan discrepancias de datos que no lograron fallar de manera explícita | Un 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:
- 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. - 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
| Criterio | Idempotency Obligatoria | Retry Automático Obligatorio | Dead Letter Separada Obligatoria | Reconciliación Diaria Obligatoria |
|---|---|---|---|---|
| El evento genera un movimiento financiero o de inventario | Sí | Sí | Sí | Sí |
| El evento es unidireccional, solo lectura (Read) | No crítico | Sí | No | No |
| Volumen superior a 500 mensajes al día | Sí | Sí | Sí | Recomendado |
| Socio externo sin SLA de alta disponibilidad | Sí | Sí, con Backoff largo | Sí | Recomendado |
| Integración entre dos objetos no financieros de bajo volumen | Recomendado | Recomendado | No necesario | No |
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
| Riesgo | Cómo se manifiesta en la práctica | Acción preventiva |
|---|---|---|
| Reintento infinito en un error estructural | El mismo mensaje falla repetidamente y genera carga | Clasificar errores de antemano y enviar errores estructurales directamente a la Dead Letter |
| Ausencia de clave única para el evento | Un reintento o una llamada duplicada crean un registro duplicado | External ID en el evento, no solo en el registro final |
| Dead Letter sin propietario | Los mensajes se acumulan y nadie los gestiona | Definir 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 coherente | Añadir reconciliación que compare el resultado de negocio, no solo el código de respuesta |
| Backoff constante y demasiado corto | Los reintentos repetidos agravan la carga durante un fallo generalizado | Exponential 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.
