← CodeClarityLab Home
Browse by Category
+ added · updated 7d
← Back to glossary

Message Idempotency

messaging Intermediate
debt(d9/e7/b7/t5)
d9 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'silent in production until users hit it' (d9). The detection_hints flag automated:no and the code_pattern hint (message_id|idempotency) is just a grep for presence of a keyword — it cannot detect absence of idempotency logic. Duplicate processing silently corrupts state or sends duplicate emails; users discover it only when they receive duplicate emails or see double-charged orders in production.

e7 Effort Remediation debt — work required to fix once spotted

Closest to 'cross-cutting refactor across the codebase' (e7). The quick_fix describes a multi-step solution: introduce unique UUIDs in every message producer, add a deduplication table with transactional checks in every consumer, and implement a key-expiry mechanism. This touches message producers, all consumer workers, and the data layer — spanning multiple components and requiring coordinated rollout rather than a single-file fix.

b7 Burden Structural debt — long-term weight of choosing wrong

Closest to 'strong gravitational pull' (b7). Applies_to covers web, cli, and queue-worker contexts — every consumer in the system must be designed with idempotency in mind. The deduplication table becomes a shared dependency, expiry logic must be maintained, and every new message type or consumer added in future must conform to the idempotency contract, shaping ongoing development across the board.

t5 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'notable trap (a documented gotcha most devs eventually learn)' (t5). The misconception field captures the canonical trap: developers believe idempotency means returning the same response, but it actually means leaving the same system state — an idempotent operation may legitimately return 409 on a duplicate. Common mistakes (using timestamps as keys, forgetting key expiry) reinforce that while this trap is documented and learnable, it consistently catches intermediate developers.

About DEBT scoring →

TL;DR

An idempotent message handler produces the same result whether called once or many times — essential for at-least-once delivery where duplicates are expected.

Explanation

Idempotency strategies: (1) Natural idempotency: UPDATE SET status = 'sent' WHERE status = 'pending' — safe to run twice. (2) Idempotency key: store message ID, skip if already processed. (3) Conditional insert: INSERT IGNORE / ON CONFLICT DO NOTHING. (4) Check-and-set: check if result already exists before creating. Design events to carry their own idempotency: 'set balance to X' (idempotent) vs 'add X to balance' (not idempotent). Include a unique message ID (UUID) in every message. Deduplication window: SQS FIFO has 5-minute dedup window by content hash.

Common Misconception

Idempotency means returning the same response — it means the system state is the same. An idempotent operation can return different responses (200 on first call, 409 on duplicate) but leaves the same state.

Why It Matters

Idempotency is the practical solution to at-least-once delivery — if you can't guarantee exactly-once at the broker level, make consumers safe to call multiple times.

Common Mistakes

  • Non-idempotent email sending — sends duplicate emails on redeliver.
  • Using timestamp as idempotency key — not unique enough.
  • Forgetting that idempotency keys need to expire (storage grows infinitely otherwise).

Code Examples

✗ Vulnerable
// Not idempotent — sends email twice on redeliver:
function handleWelcomeEmail($msg) {
    sendEmail($msg['user_email'], 'Welcome!'); // Sent twice if redelivered
    ack($msg);
}
✓ Fixed
function handleWelcomeEmail($msg) {
    $alreadySent = DB::table('sent_emails')
        ->where('message_id', $msg['id'])->exists();
    if ($alreadySent) return ack($msg);

    DB::transaction(function() use ($msg) {
        sendEmail($msg['user_email'], 'Welcome!');
        DB::table('sent_emails')->insert([
            'message_id' => $msg['id'],
            'sent_at' => now(),
        ]);
    });
    ack($msg);
}

Added 23 Mar 2026
Views 33
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 1 ping F 0 pings S 1 ping S 1 ping M 0 pings T 1 ping W 0 pings T 0 pings F 0 pings S 0 pings S 0 pings M 1 ping T 2 pings W 0 pings T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 1 ping W 1 ping T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F
No pings yet today
No pings yesterday
Perplexity 8 Amazonbot 7 Unknown AI 3 SEMrush 3 Ahrefs 2 Google 2 ChatGPT 1 Meta AI 1
crawler 24 crawler_json 1 pre-tracking 2
DEV INTEL Tools & Severity
🟠 High ⚙ Fix effort: Medium
⚡ Quick Fix
Include unique UUID in every message. Check deduplication table before processing. Use DB transactions to atomically process + record. Expire old dedup keys after retention period.
📦 Applies To
web cli queue-worker
🔗 Prerequisites
🔍 Detection Hints
message_id|idempotency
Auto-detectable: ✗ No
⚠ Related Problems
🤖 AI Agent
Confidence: Low False Positives: High ✗ Manual fix Fix: Medium Context: Function Tests: Update

✓ schema.org compliant