Idempotency, retry-with-jitter, and a dead-letter queue, as three standalone Go libraries plus a runnable HTTP demo -- fifteen ways the payments-style plumbing most real systems get subtly wrong quietly breaks.
Nobody has started this yet — be first.
rate-limited-429 simulation profile fails only one attempt instead of two
This project's entire pitch is "you can observe retry/idempotency/DLQ behavior with zero external dependencies" via the built-in flaky downstream simulator. If the simulator quietly drifts from what it's documented to do, every curl walkthrough, demo, and integration test that relies on "429 on attempts 1-2, success on attempt 3" (the README's own simulated-profiles table) gets a different attempt count than advertised. That's a small, contained blast radius today, but it's exactly the kind of drift that erodes trust in a staging/demo environment over time -- QA or a new teammate re-running the documented walkthrough gets confused by a result that doesn't match the docs, and starts questioning whether the real bug is somewhere else entirely.
About this project
A production-shaped reliability layer, as three independently importable Go libraries and a runnable HTTP service that demonstrates all of them against a deliberately flaky (but entirely in-process) simulated downstream: pkg/idempotency (Stripe-style Idempotency-Key semantics -- an operation executes at most once per key, no matter how many times the request is retried or races itself, with both a memory and a Redis backend), pkg/retry (a generic exponential-backoff-with-jitter executor, decoupled from HTTP or idempotency, usable standalone), and pkg/dlq (a dead-letter store: jobs that exhaust retries land here in full, instead of disappearing into a log line).
Ten tickets are bugs deliberately injected into an otherwise-working codebase (a sign flip in a background janitor that evicts live keys almost immediately, a Redis Lua ARGV-ordering mismatch that falsely conflicts every retried request, a zero-value bug that silently reports success without ever running the operation); five are enhancements that do not exist yet, each with a compile-only stub so the codebase and the verification tests build before the real implementation is written. Every ticket has a dedicated Go test that goes green when the fix is correct.
HTTP 500 is no longer retryable
500 Internal Server Error is the single most common status code a struggling downstream returns. If this library's retry classifier no longer treats it as retryable, every charge (or any other operation wrapped in this executor) that hits a transient 500 short-circuits immediately instead of retrying -- landing in the DLQ after exactly one attempt, during exactly the kind of brief downstream blip retries exist to absorb. That's the difference between "the customer's payment succeeded on the automatic 3rd attempt, they never noticed" and "the customer's payment failed and support has to manually explain what a dead-letter queue is."
MaxAttempts: 0docker compose up --build # app + redis, hot-reloading on save
Work the tickets in tasks/ (ordered easy → hard); each names one Go test in test/:
make test-race # go test -race -count=1 ./...
go test ./test/ -run TestTask01 -v # a single ticket
How to run it locally, step by step.