Skip to content

chore(claude): encode 'durability != replay — don't add a stream to avoid message loss'#47

Merged
emeraldleaf merged 4 commits into
mainfrom
chore/encode-streams-vs-queues-rule
Jun 3, 2026
Merged

chore(claude): encode 'durability != replay — don't add a stream to avoid message loss'#47
emeraldleaf merged 4 commits into
mainfrom
chore/encode-streams-vs-queues-rule

Conversation

@emeraldleaf

@emeraldleaf emeraldleaf commented May 28, 2026

Copy link
Copy Markdown
Owner

Summary

Encodes one distributed-systems decision-criterion bullet in CLAUDE.md "Communication Patterns," surfaced by a conversation about pub/sub message loss.

The misconception it corrects: "pub/sub loses the message if a subscriber is down, so use a stream (Kafka/Event Hubs) when you can't afford loss." Not so — loss depends on whether the subscription is durable, not on queue-vs-stream.

The rule:

  • NextAurora already can't lose a message on either side: transactional outbox (publish side) + durable Service Bus subscriptions / RabbitMQ durable queues (consume side). At-least-once delivery means the risk is duplication, not loss — hence idempotent handlers.
  • Reach for a stream only when you need replay-from-offset / multi-day retention / ordered append-only log / N independent re-reading consumers — not merely "don't lose messages."
  • NextAurora has no such need (deleted the EventLogs replay table; future replay rides Wolverine's message store). Adding a stream to prevent loss the existing stack already prevents is the same speculative over-engineering the factory-pattern rule warns against.

Paraphrase-drift audit (per CLAUDE.md change)

Run /check-rules locally to audit paraphrases against this diff. Done — grep for streams / Event Hubs / Kafka / replay / durability paraphrases found only descriptive doc mentions (event-replay.md, architecture.md, observability.md, etc.), none carrying a See CLAUDE.md marker tied to a streams/durability rule. The new bullet is consistent with the existing "Event Replay" canon (Wolverine message store, EventLogs deleted). No drift to realign.

Scope decision

  • CLAUDE.md only. Deliberately NOT added to .coderabbit.yaml or the architecture-reviewer — there's no file-pattern surface to scan ("did someone add Event Hubs?" isn't a code-shape check). This is decision-guidance, which belongs in CLAUDE.md, not a reviewable rule.
  • The load-bearing parts (outbox, idempotency, durable delivery) were already thoroughly encoded; this adds only the missing streams-vs-queues decision criterion.

Test plan

  • Paraphrase-drift audit run (see above) — no drift
  • New bullet consistent with existing "Event Replay" + "Outbox atomicity" + "Event handlers must be idempotent" rules
  • Architecture-reviewer / CodeRabbit need no new path rules (decision-guidance, no code surface)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Documentation
    • Added a "Communication Patterns" rule for async events clarifying durability vs. replay: loss prevention relies on pairing a transactional outbox with durable pub/sub subscriptions/queues, at-least-once delivery risks duplication which should be handled by idempotent handlers, and guidance now recommends using streams only when replay, retention, or ordered-log semantics are required (link to transport-selection guide).

Review Change Stack

…void message loss"

Surfaced by a conversation about pub/sub message loss. Common
misconception: "pub/sub loses the message if a subscriber is down, so
use a stream (Kafka/Event Hubs) when you can't afford loss." The
correction is a real distributed-systems decision worth encoding:

- Message loss depends on whether the subscription is DURABLE, not on
  queue-vs-stream. NextAurora already can't lose a message on either
  side: transactional outbox (publish side — entity + event in one DB
  tx, dispatched with retry) + durable Service Bus subscriptions /
  RabbitMQ durable queues (consume side — broker holds per-subscriber
  until ack). At-least-once means the risk is duplication, not loss,
  which is why handlers are idempotent.
- Reach for a stream ONLY when you need what a durable queue can't give:
  replay from an offset, multi-day retention, an ordered append-only
  log, or N independent consumers re-reading history. NOT merely "don't
  lose messages."
- NextAurora has no such need (deleted the hand-rolled EventLogs replay
  table; future replay rides Wolverine's message store). Adding a stream
  to prevent loss the outbox+durable-queue+idempotency stack already
  prevents is the same speculative over-engineering the factory-pattern
  rule warns against.

Encoded as ONE bullet in CLAUDE.md "Communication Patterns" (right after
"Async events"). Deliberately NOT added to .coderabbit.yaml or the
architecture-reviewer — there's no file-pattern surface to scan ("did
someone add Event Hubs?" isn't a code-shape check); this is
decision-guidance, which lives in CLAUDE.md, not a reviewable rule. The
load-bearing parts (outbox, idempotency, durable delivery) were already
thoroughly encoded; this adds only the missing streams-vs-queues
decision criterion.

Paraphrase-drift audit: run /check-rules locally to audit paraphrases
against this diff. Done — grep for streams/Event-Hubs/Kafka/replay/
durability paraphrases found only descriptive doc mentions
(event-replay.md, architecture.md, etc.), none carrying a See-CLAUDE.md
marker tied to a streams/durability rule; the new bullet is consistent
with the existing "Event Replay" canon. No drift to realign.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown

Walkthrough

Added a messaging pattern clarification to CLAUDE.md distinguishing durability from replay in pub/sub systems; documents transactional outbox + durable Azure Service Bus subscriptions/queues for loss avoidance and reserves streams for replay/retention/ordering scenarios.

Changes

Communication Patterns Clarification

Layer / File(s) Summary
Durability vs. replay messaging rule
CLAUDE.md
New guidance clarifying that pub/sub durability does not provide replay semantics; recommends pairing transactional outbox patterns with durable subscriptions/queues and handling at-least-once duplication via idempotent handlers.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~2 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: encoding a rule that durability is not equivalent to replay capability, and that message loss should not drive the decision to adopt streams—a key architectural guidance added to CLAUDE.md.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/encode-streams-vs-queues-rule

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov

codecov Bot commented May 28, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@CLAUDE.md`:
- Line 174: Add the required paraphrase-audit note to the CLAUDE.md change by
appending the sentence "Run /check-rules locally to audit paraphrases against
this diff." next to the rule paragraph titled "Durability ≠ replay — don't reach
for a stream just to avoid losing messages." so reviewers see the explicit
instruction; ensure the note is clearly flagged (e.g., on the same paragraph or
immediately below it) so it accompanies this rule change in the commit message
and PR description.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 90a22d60-eb43-49e2-ba3d-89857088f47b

📥 Commits

Reviewing files that changed from the base of the PR and between 6f340be and f430bd5.

📒 Files selected for processing (1)
  • CLAUDE.md

Comment thread CLAUDE.md Outdated
## Communication Patterns

- **Async events** (Azure Service Bus): For workflow orchestration (order -> payment -> shipping -> notification)
- **Durability ≠ replay — don't reach for a stream just to avoid losing messages.** A common misconception is "pub/sub loses the message if a subscriber is down, so use a stream (Kafka/Event Hubs) when you can't afford loss." Not so: message loss depends on whether the subscription is **durable**, not on queue-vs-stream. NextAurora's stack already can't lose a message on either side — the **transactional outbox** guards the publish side (event persisted in the same DB transaction as the entity write, dispatched with retry, so "entity saved but event lost" can't happen), and **durable Service Bus subscriptions / RabbitMQ durable queues** guard the consume side (the broker holds the message per-subscriber until ack, so a down service resumes from where it left off). At-least-once delivery means the real risk is *duplication, not loss* — which is why every handler is idempotent (see "Key Conventions: Event handlers must be idempotent"). **Reach for a stream (Kafka, Azure Event Hubs, Redis Streams) only when you need what a durable queue can't give: replay from an offset, multi-day retention, an ordered append-only event log, or N independent consumers each re-reading history at their own pace** — *not* merely "don't lose messages." NextAurora has no such need today (it deliberately deleted the hand-rolled `EventLogs` replay table; any future replay rides Wolverine's own message store). Adding a stream to prevent loss the outbox + durable-queue + idempotency stack already prevents is the same speculative over-engineering the factory-pattern rule warns against.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Add the required CLAUDE.md paraphrase-audit note.

Please include this explicit note with the CLAUDE.md rule change: "Run /check-rules locally to audit paraphrases against this diff."

As per coding guidelines: “When this changes… Flag the CLAUDE.md change with a note: ‘Run /check-rules locally to audit paraphrases against this diff.’”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@CLAUDE.md` at line 174, Add the required paraphrase-audit note to the
CLAUDE.md change by appending the sentence "Run /check-rules locally to audit
paraphrases against this diff." next to the rule paragraph titled "Durability ≠
replay — don't reach for a stream just to avoid losing messages." so reviewers
see the explicit instruction; ensure the note is clearly flagged (e.g., on the
same paragraph or immediately below it) so it accompanies this rule change in
the commit message and PR description.

…n the streams rule

Folds in a concrete example (from an Anton Martyniuk post) that sharpens
the durability-vs-replay rule: Redis Pub/Sub specifically IS fire-and-forget
— no persistence, a down subscriber misses the message forever — and Redis
Streams is its durable+replayable counterpart within the Redis ecosystem.
That's where the "pub/sub loses messages" intuition comes from, and it's
correct *for Redis*.

The clarification keeps the general principle intact: that's a property of
Redis Pub/Sub being non-durable, NOT of pub/sub as a pattern. Durable
pub/sub (ASB topics+subscriptions, RabbitMQ durable queues, SNS→SQS) does
not lose messages. NextAurora uses the durable kind (and Redis only as
HybridCache L2, never for messaging), so the loss-prevention guarantee
stands.

Same rule, sharper example — no paraphrase drift.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented May 28, 2026

Copy link
Copy Markdown

Actionable comments posted: 0

Adds a deep-dive link from the "Durability ≠ replay" bullet to the new
portable decision guide (docs/messaging-transport-selection.md, PR #49):
the CLAUDE.md bullet is NextAurora's specific rule; the doc is the
general Redis-Pub/Sub-vs-Streams-vs-RabbitMQ-vs-ASB-vs-SNS+SQS-vs-Kafka
matrix behind it. Pointer only — no rule change, no paraphrase drift.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
CLAUDE.md (1)

174-174: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add the required paraphrase-audit note for this CLAUDE.md rule change.

Please append this exact note alongside this rule update: "Run /check-rules locally to audit paraphrases against this diff."

As per coding guidelines: “When this changes, every paraphrase ending in "See CLAUDE.md" elsewhere in the repo may need updating. Flag the CLAUDE.md change with a note: "Run /check-rules locally to audit paraphrases against this diff."”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@CLAUDE.md` at line 174, Append the exact paraphrase-audit note "Run
/check-rules locally to audit paraphrases against this diff." immediately
alongside the updated rule block that begins "Durability ≠ replay — don't reach
for a stream just to avoid losing messages." in CLAUDE.md; ensure the sentence
is added as a separate sentence or parenthetical directly after that rule
paragraph so tooling and readers see the flag about updating paraphrases that
end with "See CLAUDE.md".
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@CLAUDE.md`:
- Line 174: Append the exact paraphrase-audit note "Run /check-rules locally to
audit paraphrases against this diff." immediately alongside the updated rule
block that begins "Durability ≠ replay — don't reach for a stream just to avoid
losing messages." in CLAUDE.md; ensure the sentence is added as a separate
sentence or parenthetical directly after that rule paragraph so tooling and
readers see the flag about updating paraphrases that end with "See CLAUDE.md".

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: ff502cd9-8af1-4fb5-9267-0008ef292537

📥 Commits

Reviewing files that changed from the base of the PR and between d145ce0 and 908a03e.

📒 Files selected for processing (1)
  • CLAUDE.md

emeraldleaf added a commit that referenced this pull request Jun 3, 2026
A reusable, system-agnostic decision guide for choosing a messaging
transport — Redis Pub/Sub, Redis Streams, RabbitMQ, Azure Service Bus,
AWS SNS+SQS, Kafka/Event Hubs/Kinesis. Written to be lifted into any
repo, with NextAurora's choice as the worked example at the end (not the
premise).

Motivation: the rule corpus is meant to be reused across systems, not
just NextAurora. A general transport-selection matrix clears that bar
(real, recurring architecture decision) where it would have been
over-encoding for NextAurora alone. Kept OUT of CLAUDE.md deliberately —
CLAUDE.md is THIS repo's operating rules; a general Kafka-vs-SNS matrix
is portable reference knowledge that belongs in a standalone, copyable
doc, not the project rulebook.

Contents:
- Step 0: do you even need a broker?
- Decision axes in order: durability → replay → ordering → throughput →
  managed-vs-self-host → existing-infra → latency
- Comparison matrix (durable? replay? ordering? throughput? hosting?
  best-for) across all six transport families
- Per-transport when-to-use / when-not notes
- Common mistakes (durability≠replay, stream-for-someday-replay, two
  messaging systems, exactly-once myth, producer-side outbox gap)
- .NET note: Wolverine/MassTransit make the transport swappable, so the
  choice is low-stakes and reversible
- Worked example: NextAurora's durable-pub/sub + outbox + idempotency
  shape, Redis-as-cache-only, no streams

README Documentation table updated to index it. No CLAUDE.md edit here
(kept separate to avoid colliding with the open streams-rule PR #47,
which is where the in-repo deep-dive pointer will be added).

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
@emeraldleaf

Copy link
Copy Markdown
Owner Author

Run /check-rules locally to audit paraphrases against this diff.

@emeraldleaf
emeraldleaf merged commit 6f5cf52 into main Jun 3, 2026
7 checks passed
@emeraldleaf
emeraldleaf deleted the chore/encode-streams-vs-queues-rule branch June 3, 2026 00:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant