Cuando falla un proceso de varios pasos, volver a ejecutarlo entero puede repetir efectos que ya ocurrieron: crear dos pedidos, enviar dos notificaciones o importar dos veces el mismo registro. La recuperación parcial de procesos fallidos en PHP consiste en determinar qué pasos están confirmados, cuáles pueden repetirse sin riesgo y cuáles requieren compensación o revisión humana.
La decisión depende de la semántica de cada operación, no solo de dónde se produjo una excepción. Una respuesta perdida de una API, por ejemplo, no demuestra que el sistema externo haya rechazado la solicitud. El proceso pudo completarse y la conexión fallar antes de que PHP recibiera el resultado. Diseñar para ese caso evita confundir una ejecución interrumpida con una ejecución inexistente.
Elegir entre reintentar, reanudar y compensar

Un reintento completo vuelve a ejecutar todos los pasos. Es apropiado cuando el flujo entero es idempotente —repetirlo deja el mismo estado final— o cuando aún no se produjeron efectos externos. Si no se puede garantizar ninguna de estas condiciones, repetirlo sin inspección es arriesgado.
Reanudar significa continuar desde el primer paso que no está confirmado. Requiere registrar el estado de los pasos y sus resultados, además de poder recuperar o verificar el efecto de una llamada cuyo resultado sea ambiguo. No equivale a saltarse todo lo que parece completado: hace falta evidencia persistida.
Compensar consiste en ejecutar una acción que contrarresta un efecto anterior, como cancelar una reserva. No siempre restaura exactamente el estado original: una notificación ya enviada no puede retirarse y un pago capturado puede requerir un reembolso con su propio plazo y registro. Por eso, una compensación es una operación de negocio explícita, no un rollback automático de la base de datos.
Modelar pasos con estados y resultados persistidos
Representa el flujo como una secuencia o máquina de estados cuyos pasos tengan nombres estables, entradas identificables y resultados persistidos. Un modelo inicial podría incluir estados como pending, running, succeeded, retryable, failed y manual_review. Define las transiciones permitidas y evita que un proceso cambie a un estado final sin guardar la evidencia necesaria.
Un registro por proceso puede incluir un identificador estable, el tipo de flujo, el estado global, la versión de la definición del proceso, las fechas de inicio y actualización, el intento y el motivo de la última transición. Cada paso debe guardar su estado, un identificador de operación, marcas temporales y una referencia al resultado relevante. Persiste solo la información necesaria para reanudar o explicar el resultado; no copies indiscriminadamente respuestas completas de APIs ni secretos.
En PHP, el coordinador puede separar la transición de estado de la ejecución del paso. La actualización debe ser atómica cuando varios workers puedan tomar el mismo trabajo: usa una transacción o un mecanismo de bloqueo apropiado y registra quién reclamó el proceso y hasta cuándo. Un bloqueo con vencimiento debe permitir recuperar trabajos abandonados sin dar por completado un paso que quedó a medias.
Definir puntos de control sin asumir “exactamente una vez”
Guarda un punto de control después de cada resultado que el sistema pueda confirmar de forma fiable. Para operaciones locales, esto puede ser una transacción que guarde juntos el cambio de negocio y el estado del paso. Para una llamada externa, no existe una transacción común entre la base de datos y el proveedor: el proceso puede detenerse después de que el proveedor actúe y antes de que PHP registre la respuesta.
En ese límite, usa una clave de idempotencia si el proveedor la admite, derivada de un identificador estable del proceso y del paso. Si no hay soporte, consulta el estado remoto mediante un identificador de operación antes de repetir la solicitud. Cuando no exista ni idempotencia ni consulta fiable, trata el resultado como ambiguo y deriva el caso a revisión. Un timeout no basta para concluir que la operación no ocurrió.
Para tareas asíncronas, el patrón de bandeja de salida transaccional (outbox) permite guardar el cambio local y el mensaje pendiente dentro de una misma transacción. Un worker entrega luego el mensaje; el consumidor también debe tolerar duplicados, por ejemplo, guardando los identificadores procesados. Estos mecanismos reducen inconsistencias, pero no convierten automáticamente toda una integración distribuida en una operación atómica.
Establecer límites de reejecución y compensación
Define por paso qué errores son transitorios, cuáles son definitivos y cuáles dejan un resultado desconocido. Los fallos transitorios pueden admitir reintentos con espera creciente y variación aleatoria; fija un máximo de intentos y un plazo total. Errores de validación o permisos no suelen mejorar con reintentos: conviene detener el flujo, corregir la causa y decidir si procede una nueva ejecución.
Documenta para cada efecto externo si se puede repetir, consultar, compensar o no revertir. Conserva el resultado cuando la operación sea válida y la repetición sea más dañina que el estado parcial; compensa solo si existe una acción de negocio segura y autorizada. Detén y escala cuando los datos no permitan determinar qué ocurrió, la compensación también falle o la acción tenga impacto financiero, legal o sobre clientes que requiera aprobación.
Una política de compensación debe especificar orden, condiciones, responsable y resultado esperado. Registra la compensación como un paso nuevo, vinculado al efecto original, en lugar de borrar su historial. Así, operaciones puede distinguir entre una acción nunca realizada, una acción realizada y otra compensada.
Dar a operaciones controles y contexto para actuar
La consola o procedimiento operativo debe mostrar el estado global y por paso, el último error clasificado, el número de intentos, las referencias externas y la acción permitida. Evita ofrecer un botón genérico de “reintentar todo”. Presenta opciones acotadas: reintentar un paso idempotente, consultar el estado remoto, ejecutar una compensación o escalar.
Protege esas acciones con autorización basada en roles; exige confirmación adicional para efectos sensibles y registra quién actuó, cuándo, qué eligió y por qué. Si la reejecución cambia datos de entrada, obliga a crear una nueva ejecución o revisión explícita en vez de alterar silenciosamente la entrada de un proceso histórico.
Para diagnosticar sin exponer información sensible, conserva identificadores de correlación, códigos de error, versión del proceso y referencias necesarias para consultar sistemas de origen. Redacta tokens, datos personales y cargas completas. Define también cuánto tiempo se guardan registros y quién puede consultarlos. Una traza útil explica qué sucedió sin convertirse en una copia innecesaria de los datos de negocio.
Probar fallos y desplegar la recuperación gradualmente
Prueba interrupciones en puntos concretos: antes de ejecutar un paso, después de que el proveedor actúe pero antes de guardar la respuesta, durante una compensación y mientras dos workers intentan reclamar el mismo proceso. Verifica que el estado sea coherente tras cada caso, que los efectos no se dupliquen y que una acción manual quede auditada.
Incluye pruebas para respuestas ambiguas, claves de idempotencia repetidas, datos inválidos, límites de reintentos y cambios de versión del flujo. Las pruebas de integración con dependencias simuladas pueden reproducir fallos controlados; cuando el proveedor real tenga comportamientos distintos, valida también el contrato y los mecanismos de consulta en un entorno adecuado.
Para un flujo existente, empieza clasificando sus pasos por reversibilidad e idempotencia. Después persiste el estado de una etapa limitada, implementa recuperación para los fallos de mayor riesgo y observa los casos pendientes antes de ampliar el alcance. No borres ni reinicies registros históricos para facilitar el despliegue: conserva la trazabilidad y define cómo interpretar procesos creados con versiones anteriores.
Lista de comprobación para implantar la recuperación

- ¿Cada paso tiene entradas identificables, un estado persistido y un resultado verificable?
- ¿Se sabe qué llamadas son idempotentes y qué hacer si su resultado es ambiguo?
- ¿Hay límites de intentos, plazos y clasificación de errores?
- ¿Las compensaciones están definidas como acciones de negocio, con auditoría y responsable?
- ¿Operaciones puede consultar y actuar con permisos adecuados, sin acceder a datos innecesarios?
- ¿Se han probado fallos entre pasos, concurrencia, reejecuciones y compensaciones fallidas?
- ¿Existe un procedimiento de escalado cuando no es seguro reanudar automáticamente?
El criterio práctico es conservar suficiente evidencia para decidir el siguiente paso y detener la automatización cuando esa evidencia no alcance. Una recuperación segura no intenta ocultar que hubo un fallo: hace explícito qué se completó, qué queda pendiente y quién puede resolverlo.



