Practice-inspired educational scenario. This is a model case, not a report of a confirmed incident.
Situation
A service saves an order and calls a mail service within the same request. The order receives a number and the database transaction commits. Email delivery times out, so the API returns a 500 error. The user is told to try again even though the first order already exists. In this process the message is informational and is not a condition for accepting the order.
Where the anti-pattern appears
An auxiliary technical effect controls the reported business outcome. The system cannot express two separate facts: “order accepted” and “confirmation pending”. Its retry scope is therefore too broad. It retries order creation instead of repairing notification delivery.
Warning signs
- an endpoint reports failure after the main transaction has committed;
- the user receives no identifier for the existing order and is told to try again;
- email is sent synchronously without a separate state or retry owner;
- monitoring does not distinguish order-acceptance failure from notification failure;
- a team proposes a queue but cannot explain a failure between the database commit and message publication.
Consequences
Duplicate orders can be created and customer support receives contradictory evidence. Increasing retries makes matters worse because the wrong part of the process is repeated. An ordinary queue publish after the database write can also lose the task if the process terminates between the two writes.
A better decision
- Define when the business operation is accepted and return its truthful result with an identifier.
- Record the intent to notify durably. A task record in the same transaction or a transactional outbox closes the gap between the database and broker.
- Retry delivery only. Use a stable identifier, bounded attempts, backoff and an idempotent consumer or deduplication.
- Expose operational state: pending tasks, oldest age, attempt counts and permanent errors. Route exhausted tasks to intervention rather than oblivion.
- Test mail failure, process termination immediately after commit and duplicate publication. Expect one order and controlled notification delivery.
Boundary
If message delivery is a legal or business condition for acceptance, the process needs a different contract. Do not invent that condition merely because two calls were convenient in one request thread.
Retry the failed effect, not the completed business operation.
Discuss this article
Share a constructive observation or an anonymised example. Comments are checked by AI before publication. Uncertain cases go to the moderator.