A stream is not a list, and XADD is not RPUSH

A list used as a queue loses the message when a consumer crashes mid-work, because BRPOP removes it and there is no record that anything was in flight.

# list: pop is destructive, in-flight means gone
RPUSH jobs '{"order":91204}'
BRPOP jobs 0

# stream: append-only log, entries have ids, reading does not remove
XADD orders '*' order 91204 total 4900
XLEN orders
XRANGE orders - + COUNT 10

The * asks Redis to generate the id from the current millisecond plus a sequence number, which makes ids sortable and unique. Entries stay after being read, which is the property that makes replay and multiple independent consumers possible — and also means the stream grows forever unless something trims it. The field-value pairs are flat rather than nested, so anything structured is still your own serialisation problem.