<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Akshat Soni</title>
    <description>The latest articles on Forem by Akshat Soni (@akshatsoni26).</description>
    <link>https://forem.com/akshatsoni26</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1330752%2F64c5a2c1-7e61-451f-8345-3e88b06d7f05.jpg</url>
      <title>Forem: Akshat Soni</title>
      <link>https://forem.com/akshatsoni26</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/akshatsoni26"/>
    <language>en</language>
    <item>
      <title>5-Minute AI Jobs and Closed Tabs — Why We Built Replay-Then-Tail SSE</title>
      <dc:creator>Akshat Soni</dc:creator>
      <pubDate>Sat, 02 May 2026 16:25:59 +0000</pubDate>
      <link>https://forem.com/akshatsoni26/5-minute-ai-jobs-and-closed-tabs-why-we-built-replay-then-tail-sse-2fn1</link>
      <guid>https://forem.com/akshatsoni26/5-minute-ai-jobs-and-closed-tabs-why-we-built-replay-then-tail-sse-2fn1</guid>
      <description>&lt;p&gt;We had a feature in production where a single user request could run for &lt;strong&gt;five-plus minutes&lt;/strong&gt; — fetch documents, chunk them, hit an LLM per chunk, synthesize a final answer. We did the obvious thing first: a &lt;a href="https://fastapi.tiangolo.com/" rel="noopener noreferrer"&gt;FastAPI&lt;/a&gt; handler that ran the pipeline and streamed progress back to the browser over &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events" rel="noopener noreferrer"&gt;Server-Sent Events&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Naive in-handler version — what we wrote first, before learning the
# work inside an SSE generator dies with the connection. The helpers
# (fetch_text, chunk_text, summarize_chunk) are real — they live in
# app/pipeline.py and survived the refactor. What got replaced is the
# in-handler shape of the orchestrator below.
&lt;/span&gt;
&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/jobs/stream&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;gen&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;event&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;started&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;event&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fetched&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; chars&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;chunk_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_chunks&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;summaries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;summary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;summarize_chunk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# ← ~30s each
&lt;/span&gt;            &lt;span class="n"&gt;summaries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;summary&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;event&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;chunk&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;# Naive synthesis: one more pass through the same helper.
&lt;/span&gt;        &lt;span class="n"&gt;final&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;summarize_chunk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;summaries&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;=-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;event&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;done&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;final&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;EventSourceResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;gen&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It worked beautifully — until users started doing user things. &lt;strong&gt;Closing the tab. Refreshing the page. Switching networks on the train.&lt;/strong&gt; Every disconnect cost us:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The whole 5-minute pipeline got cancelled — work the LLM had already done was thrown away.&lt;/li&gt;
&lt;li&gt;Any progress events not yet flushed to the browser vanished.&lt;/li&gt;
&lt;li&gt;When the user came back two minutes later, there was nothing to come back to. They just saw "Loading…" forever.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This post is about the architecture we ended up with to fix that, and a couple of subtle bugs we hit along the way. The whole thing is &lt;strong&gt;stack-agnostic&lt;/strong&gt; — I'll show one Python implementation, but the same shape works anywhere you have background workers and concurrent event producers (Go, Node, Rust, JVM, .NET — same pattern). Working repo: &lt;strong&gt;&lt;a href="https://github.com/AkshatSoni26/longshot" rel="noopener noreferrer"&gt;github.com/AkshatSoni26/longshot&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  What — what we wanted
&lt;/h2&gt;

&lt;p&gt;A pipeline that satisfies four properties:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The work outlives the connection.&lt;/strong&gt; Closing the tab must not cancel a 5-minute LLM job.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Progress survives disconnects.&lt;/strong&gt; A client that comes back ten seconds — or ten minutes — later sees everything that happened while they were away.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Duplicate-delivery suppression.&lt;/strong&gt; If the same task is delivered twice (it will be — more on that later), the second delivery becomes a silent no-op. &lt;em&gt;True&lt;/em&gt; exactly-once over partial failures needs checkpointing, which we'll be honest about at the end.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live updates while the client is connected.&lt;/strong&gt; Polling &lt;code&gt;/status&lt;/code&gt; every two seconds is a non-answer.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fanbeqkv70kz24ao41gpt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fanbeqkv70kz24ao41gpt.png" alt="replay-then-tail-architecture" width="800" height="281"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The trick is the separation: &lt;strong&gt;the work flows through the queue and the worker&lt;/strong&gt;, &lt;strong&gt;the client connects to the stream of events&lt;/strong&gt; the worker produces. Client and worker never share a request lifecycle.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why — why naive SSE can't fix this
&lt;/h2&gt;

&lt;p&gt;Before we dig into the architecture, it's worth being precise about why the naive handler above doesn't survive disconnects.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/sysid/sse-starlette" rel="noopener noreferrer"&gt;&lt;code&gt;EventSourceResponse&lt;/code&gt;&lt;/a&gt; (and the equivalent in any other framework) is an async generator running inside the request's task. When the underlying TCP connection closes — tab close, refresh, network blip — the framework stops driving the generator. The next &lt;code&gt;await&lt;/code&gt; inside it raises (the exact exception is &lt;code&gt;CancelledError&lt;/code&gt;, &lt;code&gt;anyio.ClosedResourceError&lt;/code&gt;, or &lt;code&gt;ClientDisconnect&lt;/code&gt; depending on framework version) and the generator unwinds.&lt;/p&gt;

&lt;p&gt;If your business logic lives &lt;strong&gt;inside that generator&lt;/strong&gt; (like the snippet above), it dies with it. The LLM call mid-flight is cancelled. The state in the function's locals is gone. The next reconnect creates a &lt;em&gt;fresh&lt;/em&gt; request. (The HTML5 EventSource spec lets the browser send a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/EventSource/lastEventId" rel="noopener noreferrer"&gt;&lt;code&gt;Last-Event-ID&lt;/code&gt;&lt;/a&gt; header on auto-reconnect — but it's a hint the server has to implement against, not built-in state continuity. We're going to build that server-side mechanism.) Each request is otherwise independent.&lt;/p&gt;

&lt;p&gt;This is correct HTTP behavior. &lt;strong&gt;You can't change it, and you shouldn't want to.&lt;/strong&gt; The fix is to make sure the work &lt;em&gt;isn't&lt;/em&gt; inside the request task in the first place. That's the wedge the rest of the architecture drives in.&lt;/p&gt;

&lt;p&gt;A naive variant gets you closer but still loses progress: move the work to a background task (&lt;a href="https://docs.celeryq.dev/" rel="noopener noreferrer"&gt;Celery&lt;/a&gt;, &lt;a href="https://docs.bullmq.io/" rel="noopener noreferrer"&gt;BullMQ&lt;/a&gt;, &lt;a href="https://taskiq-python.github.io/" rel="noopener noreferrer"&gt;TaskIQ&lt;/a&gt;, &lt;a href="https://github.com/sidekiq/sidekiq" rel="noopener noreferrer"&gt;Sidekiq&lt;/a&gt;, whatever), publish progress to a &lt;a href="https://redis.io/docs/manual/pubsub/" rel="noopener noreferrer"&gt;Redis Pub/Sub&lt;/a&gt; channel, have the SSE endpoint subscribe to that channel. Now the worker survives the disconnect. &lt;strong&gt;But Pub/Sub is at-most-once and has no replay.&lt;/strong&gt; If a client is offline when an event fires, that event is gone forever. The user comes back, the SSE endpoint subscribes, and the next thing they see is whatever the worker emits next — with no idea what they missed.&lt;/p&gt;

&lt;p&gt;That trichotomy of needs — durable history, latest-state snapshot, live tail — is what drives the design.&lt;/p&gt;




&lt;h2&gt;
  
  
  How — three storage shapes for one event
&lt;/h2&gt;

&lt;p&gt;For every progress event the worker emits, we write it to &lt;strong&gt;three Redis structures in one pipelined round-trip&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Shape&lt;/th&gt;
&lt;th&gt;Key&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Replay list&lt;/td&gt;
&lt;td&gt;&lt;a href="https://redis.io/commands/rpush/" rel="noopener noreferrer"&gt;&lt;code&gt;RPUSH events:{sid}&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;A reconnecting client reads the whole list to catch up. (How "durable" this is depends on your Redis persistence config — AOF/RDB. The events list is a streaming buffer, not a journal of record; for that, write to your application database.)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Snapshot&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://redis.io/commands/set/" rel="noopener noreferrer"&gt;&lt;code&gt;SET snapshot:{sid}&lt;/code&gt;&lt;/a&gt; &lt;code&gt;EX 3600&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;A &lt;em&gt;late&lt;/em&gt; joiner can fetch current state in one call without scanning the list&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Live channel&lt;/td&gt;
&lt;td&gt;&lt;a href="https://redis.io/commands/publish/" rel="noopener noreferrer"&gt;&lt;code&gt;PUBLISH channel:{sid}&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Already-connected clients get the new event instantly&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Plus an &lt;a href="https://redis.io/commands/incr/" rel="noopener noreferrer"&gt;&lt;code&gt;INCR seq:{sid}&lt;/code&gt;&lt;/a&gt; for a monotonic per-session sequence number — the client uses this to detect gaps and dedupe between the replay and the live tail.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A note on the snapshot key.&lt;/strong&gt; The demo writes the snapshot for completeness — it's the third leg of the trichotomy and a pattern worth showing. But the SSE endpoint we'll build below replays the list (the full timeline), not the snapshot. The snapshot is more useful when you have a &lt;em&gt;cheap status endpoint&lt;/em&gt; that returns "what's happening right now" without streaming, e.g. a polling-only dashboard or a job-overview screen. Drop it if your client-side never calls anything but the streaming endpoint.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;None of this is Redis-specific.&lt;/strong&gt; The same trichotomy is "Kafka log + materialized view + websocket fanout", "Postgres &lt;code&gt;LISTEN/NOTIFY&lt;/code&gt; + a state row + a streaming view", or "NATS &lt;a href="https://docs.nats.io/nats-concepts/jetstream" rel="noopener noreferrer"&gt;JetStream&lt;/a&gt; + KV bucket + subject". I'll use Redis throughout for concreteness; substitute as needed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The producer side is conceptually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;seq&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;incr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;seq:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;seq&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;seq&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;event_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rpush&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;events:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;snapshot:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;channel:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(There's a subtle bug in there. We'll come back to it.)&lt;/p&gt;




&lt;h2&gt;
  
  
  How — the replay-then-tail SSE endpoint
&lt;/h2&gt;

&lt;p&gt;This is the half that solves the disconnect problem. The SSE handler does &lt;strong&gt;two phases&lt;/strong&gt;, but the &lt;em&gt;order&lt;/em&gt; matters in a way that's easy to get wrong:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# from app/api.py — verbatim. keys.X helpers live in app/keys.py;
# settings.X comes from a Pydantic BaseSettings in app/settings.py.
&lt;/span&gt;
&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/jobs/{session_id}/stream&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;stream_job&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;redis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;get_redis&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_settings&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;event_source&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="c1"&gt;# Order matters: SUBSCRIBE first, LRANGE second.
&lt;/span&gt;        &lt;span class="c1"&gt;#
&lt;/span&gt;        &lt;span class="c1"&gt;# If we LRANGE first then SUBSCRIBE, events published in the gap
&lt;/span&gt;        &lt;span class="c1"&gt;# between the two operations are *lost* — Redis Pub/Sub has no replay,
&lt;/span&gt;        &lt;span class="c1"&gt;# and the events are gone from the channel before we ever attached.
&lt;/span&gt;        &lt;span class="c1"&gt;# By subscribing first, redis-py starts buffering messages on the
&lt;/span&gt;        &lt;span class="c1"&gt;# connection. We then read the durable history with LRANGE; any event
&lt;/span&gt;        &lt;span class="c1"&gt;# that appears in BOTH the list and the buffered channel feed is
&lt;/span&gt;        &lt;span class="c1"&gt;# caught by the dedupe set below. Result: nothing is lost, nothing is
&lt;/span&gt;        &lt;span class="c1"&gt;# duplicated, regardless of how the producer's RPUSH/PUBLISH calls
&lt;/span&gt;        &lt;span class="c1"&gt;# interleave with our subscribe and read.
&lt;/span&gt;        &lt;span class="n"&gt;pubsub&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pubsub&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;pubsub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;loop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_event_loop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;heartbeat_at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sse_heartbeat_seconds&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;# Phase 1: REPLAY everything that's already happened. The list is
&lt;/span&gt;            &lt;span class="c1"&gt;# capped by TTL but otherwise complete — clients reconnecting
&lt;/span&gt;            &lt;span class="c1"&gt;# mid-task see the full history before the live tail starts.
&lt;/span&gt;            &lt;span class="n"&gt;replayed_seqs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;backlog_raw&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;bytes&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lrange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;events_list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;backlog_raw&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;EVENT_ADAPTER&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;validate_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;replayed_seqs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;seq&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;event&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model_dump_json&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;TERMINAL_TYPES&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="k"&gt;return&lt;/span&gt;

            &lt;span class="c1"&gt;# Phase 2: TAIL — drain the buffered messages + live ones. Dedupe
&lt;/span&gt;            &lt;span class="c1"&gt;# by seq for events that landed in both the list (from LRANGE)
&lt;/span&gt;            &lt;span class="c1"&gt;# and the channel buffer (between SUBSCRIBE and LRANGE).
&lt;/span&gt;            &lt;span class="c1"&gt;#
&lt;/span&gt;            &lt;span class="c1"&gt;# Design choice: we deliberately do NOT cancel the worker when the
&lt;/span&gt;            &lt;span class="c1"&gt;# SSE client disconnects. The whole point of replay-then-tail is
&lt;/span&gt;            &lt;span class="c1"&gt;# that the work outlives the connection — closing a tab during a
&lt;/span&gt;            &lt;span class="c1"&gt;# 5-minute job must not kill the job. The user can reconnect to
&lt;/span&gt;            &lt;span class="c1"&gt;# the same session_id and pick up exactly where they left off.
&lt;/span&gt;            &lt;span class="c1"&gt;# Explicit cancellation is via DELETE /jobs/{session_id} only.
&lt;/span&gt;            &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_disconnected&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
                    &lt;span class="c1"&gt;# Just exit the SSE generator. Do NOT set the cancel flag —
&lt;/span&gt;                    &lt;span class="c1"&gt;# the worker keeps running, and the events list keeps
&lt;/span&gt;                    &lt;span class="c1"&gt;# accumulating for the next reconnect.
&lt;/span&gt;                    &lt;span class="k"&gt;return&lt;/span&gt;

                &lt;span class="n"&gt;timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;heartbeat_at&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
                &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;pubsub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ignore_subscribe_messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;event&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;keepalive&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
                    &lt;span class="n"&gt;heartbeat_at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sse_heartbeat_seconds&lt;/span&gt;
                    &lt;span class="k"&gt;continue&lt;/span&gt;

                &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;EVENT_ADAPTER&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;validate_json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;seq&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;replayed_seqs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="k"&gt;continue&lt;/span&gt;
                &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;event&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model_dump_json&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;TERMINAL_TYPES&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;pubsub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unsubscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;pubsub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;aclose&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;EventSourceResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;event_source&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three production details that earn their lines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Keepalives.&lt;/strong&gt; SSE over a load balancer or reverse proxy will get killed silently by an idle-connection timeout if nothing flows for 30–60 seconds. Emitting a &lt;code&gt;keepalive&lt;/code&gt; event when the channel is quiet (the &lt;code&gt;msg is None&lt;/code&gt; branch) keeps the socket warm.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Disconnect detection without cancel.&lt;/strong&gt; When the client goes away we exit the SSE generator immediately, but we do &lt;em&gt;not&lt;/em&gt; set a cancel flag for the worker. The worker keeps running; the events list keeps accumulating; the user comes back, hits the same &lt;code&gt;/jobs/{id}/stream&lt;/code&gt;, and gets the full replay. Tab-close isn't a kill signal — that's the whole thesis of the post.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;finally&lt;/code&gt; cleanup.&lt;/strong&gt; The pubsub object holds a Redis connection; without explicit unsubscribe + close we'd leak it on every disconnect.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why subscribe-first matters.&lt;/strong&gt; If you LRANGE first then SUBSCRIBE — which is how almost everyone writes this on the first try, including me — there's a race window between the two operations. Any event published in that window is &lt;em&gt;gone forever&lt;/em&gt;: Pub/Sub has no replay, and the message is dropped because nobody was listening yet. Subscribing first means the connection holds incoming messages until we read them, so the LRANGE-then-drain order catches everything. The dedupe set then handles events that landed in both the list (via LRANGE) and the connection's queue (via the live subscription) — which is the only case where the same event shows up twice. If you skip the subscribe-first step, the dedupe never actually fires; it's protecting against the wrong race.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is the code that &lt;em&gt;fixes the disconnect bug we started with&lt;/em&gt;. Walk through what happens to a user whose tab closed at minute 3:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Worker is on chunk 5 of 8. The SSE generator in their first request died at minute 3, but the worker is still running because it's a separate process.&lt;/li&gt;
&lt;li&gt;User opens the tab again. Browser hits &lt;code&gt;/jobs/{sid}/stream&lt;/code&gt; — a brand new request, brand new generator.&lt;/li&gt;
&lt;li&gt;The handler subscribes to the channel first (start buffering anything new).&lt;/li&gt;
&lt;li&gt;Phase 1 reads the events list from start. The user instantly sees: &lt;code&gt;started&lt;/code&gt;, &lt;code&gt;fetch_started&lt;/code&gt;, &lt;code&gt;fetch_done&lt;/code&gt;, &lt;code&gt;chunk_summarized&lt;/code&gt; (×4) — everything that happened while they were gone.&lt;/li&gt;
&lt;li&gt;Phase 2 drains the channel buffer and tails live. They start receiving &lt;code&gt;chunk_summarized&lt;/code&gt; for chunk 5, then 6, 7, 8, then &lt;code&gt;final_started&lt;/code&gt;, &lt;code&gt;final_done&lt;/code&gt;, &lt;code&gt;done&lt;/code&gt; as they happen live.&lt;/li&gt;
&lt;li&gt;The dedupe set catches anything that was in both the LRANGE result &lt;em&gt;and&lt;/em&gt; the channel buffer — for example, an event that fired exactly between the SUBSCRIBE and the LRANGE call.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;They missed nothing. They didn't even notice the disconnect happened.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  How — at-least-once means duplicates will arrive on a Tuesday
&lt;/h2&gt;

&lt;p&gt;There's a piece I haven't shown yet that's invisible but load-bearing. Putting the work behind a durable queue is great, but it introduces a new problem: &lt;strong&gt;at-least-once delivery&lt;/strong&gt;. &lt;a href="https://redis.io/docs/data-types/streams/" rel="noopener noreferrer"&gt;Redis Streams&lt;/a&gt; doesn't auto-redeliver on its own — but with a &lt;a href="https://redis.io/docs/data-types/streams/#consumer-groups" rel="noopener noreferrer"&gt;consumer group&lt;/a&gt;, a message that's been read but not ACKed enters a &lt;em&gt;pending&lt;/em&gt; state, claimable by another consumer via &lt;a href="https://redis.io/commands/xautoclaim/" rel="noopener noreferrer"&gt;&lt;code&gt;XAUTOCLAIM&lt;/code&gt;&lt;/a&gt; (or &lt;code&gt;XPENDING&lt;/code&gt; + &lt;code&gt;XCLAIM&lt;/code&gt;) after &lt;code&gt;idle_timeout&lt;/code&gt;. The broker's reclaim loop is what actually moves the message back into circulation. Same pattern in Kafka (offset commits), RabbitMQ (manual ack), &lt;a href="https://docs.bullmq.io/" rel="noopener noreferrer"&gt;BullMQ&lt;/a&gt; (stalled-job recovery), &lt;a href="https://github.com/sidekiq/sidekiq" rel="noopener noreferrer"&gt;Sidekiq&lt;/a&gt;, most decent queues. Most retry middlewares (TaskIQ's &lt;code&gt;SmartRetryMiddleware&lt;/code&gt;, Celery's &lt;code&gt;autoretry_for&lt;/code&gt;, etc.) add another layer of redelivery on top of that for transient errors.&lt;/p&gt;

&lt;p&gt;You will get the same task fired twice. Not theoretically. In production. On a Tuesday.&lt;/p&gt;

&lt;p&gt;The fix is one line at the top of every task:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# from app/tasks.py — verbatim.
&lt;/span&gt;
&lt;span class="nd"&gt;@broker.task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;run_job&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_job&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# noqa: A002  the payload field name in the API is also `input`
&lt;/span&gt;    &lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;JobMode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;chunk_size_chars&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;max_chunks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Redis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TaskiqDepends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;redis_dependency&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;  &lt;span class="c1"&gt;# noqa: B008  TaskIQ DI mirrors FastAPI Depends
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;TaskResult&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Dispatch on ``mode``. Both branches share the lock + cancel + timeout
    skeleton — only the inner pipeline differs.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_settings&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;publisher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_publisher&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 1. Idempotency lock. If we lose the race, this is a redelivery — exit clean.
&lt;/span&gt;    &lt;span class="n"&gt;got_lock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;idempotency_lock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;nx&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;idempotency_lock_ttl_seconds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;got_lock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;TaskResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;TaskStatus&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DUPLICATE_DELIVERY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 2. ... pipeline work ...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;SET ... NX EX&lt;/code&gt; is atomic on the Redis side — only the first delivery wins the lock. While the lock is alive, every subsequent delivery observes it and exits silently. &lt;strong&gt;This is duplicate-delivery suppression, not true exactly-once.&lt;/strong&gt; It guarantees the task body doesn't &lt;em&gt;re-enter&lt;/em&gt; while a previous delivery is still working, which is what protects you from the common case (a worker briefly stalls, the broker redelivers, both versions are now alive). It does &lt;em&gt;not&lt;/em&gt; protect against:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A worker crashing after partially calling an LLM (the charge is already incurred); a future redelivery wouldn't repeat the charge while the lock holds, but the partial work is lost.&lt;/li&gt;
&lt;li&gt;The lock TTL expiring while the original task is still running (rare in practice if you tune the TTL &amp;gt; worst-case task duration, but possible).&lt;/li&gt;
&lt;li&gt;Side effects already committed before a crash being "rolled back" — they aren't.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the demo's failure modes, duplicate-delivery suppression is enough. True exactly-once over partial failures needs a checkpointed state machine, which is in &lt;em&gt;what this isn't&lt;/em&gt; below.&lt;/p&gt;




&lt;h2&gt;
  
  
  How — and now, the subtle bug from earlier
&lt;/h2&gt;

&lt;p&gt;Remember this snippet from a few sections back?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;seq&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;incr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;seq:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;seq&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;seq&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;event_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rpush&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;events:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;snapshot:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;channel:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once we deployed the architecture above and started actually using it, we noticed something weird in the event log:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;seq=1  started
seq=2  fetch_started
seq=4  chunk_summarized   index=0
seq=3  fetch_done
seq=5  chunk_summarized   index=1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;seq=4&lt;/code&gt; arrived before &lt;code&gt;seq=3&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It's a quiet bug. The browser still rendered something. The summary still ended up correct. The dedupe in the SSE handler still worked. But the contract our API advertised — &lt;em&gt;strictly monotonic seqs so the client can detect gaps&lt;/em&gt; — was silently broken.&lt;/p&gt;

&lt;p&gt;The cause is &lt;strong&gt;concurrency between emit calls&lt;/strong&gt;. The mechanism differs by runtime — Python's &lt;code&gt;asyncio&lt;/code&gt; and Node's event loop interleave at every &lt;code&gt;await&lt;/code&gt; on a single thread; Go's scheduler runs goroutines on multiple OS threads; &lt;a href="https://tokio.rs/" rel="noopener noreferrer"&gt;Tokio&lt;/a&gt;'s default runtime is multi-thread too — but the bug shape is the same in all of them. If the &lt;em&gt;only&lt;/em&gt; thing your worker does is emit events sequentially, you're fine. The instant anything else creates concurrency — a heartbeat coroutine, parallel chunk processing, an LLM streaming response triggering token events alongside another emit source — two &lt;code&gt;emit&lt;/code&gt; calls can be in flight at the same time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;coroutine A: INCR -&amp;gt; 3
coroutine B: INCR -&amp;gt; 4
coroutine B: RPUSH (seq=4 lands in the list first)
coroutine A: RPUSH (seq=3 lands second)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same bug shape in &lt;a href="https://go.dev/tour/concurrency/1" rel="noopener noreferrer"&gt;Go with goroutines&lt;/a&gt;, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all" rel="noopener noreferrer"&gt;Node with &lt;code&gt;Promise.all&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://docs.rs/tokio/latest/tokio/macro.spawn.html" rel="noopener noreferrer"&gt;Rust with &lt;code&gt;tokio::spawn&lt;/code&gt;&lt;/a&gt;. It's a property of any pattern where (a) multiple producers can emit, (b) the seq is generated &lt;em&gt;before&lt;/em&gt; the writes, and (c) the writes aren't instantaneous.&lt;/p&gt;




&lt;h2&gt;
  
  
  How — three fixes that look right but aren't
&lt;/h2&gt;

&lt;p&gt;When developers first hit this, they reach for one of three tools.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Wrap the writes in &lt;a href="https://redis.io/docs/interact/transactions/" rel="noopener noreferrer"&gt;&lt;code&gt;MULTI&lt;/code&gt;/&lt;code&gt;EXEC&lt;/code&gt;&lt;/a&gt;.&lt;/strong&gt; Doesn't help — the seq is generated &lt;em&gt;outside&lt;/em&gt; the transaction. Two emits are still independent transactions that interleave at the boundary.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Move it to a &lt;a href="https://redis.io/commands/eval/" rel="noopener noreferrer"&gt;Lua script&lt;/a&gt;.&lt;/strong&gt; Works, but it's the wrong tool. Each session's events are produced by exactly one worker (the one that won the message from the consumer group). There's no distributed problem; reaching for a server-side primitive to fix an in-process race is operationally heavy and obscures the design issue.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Distributed lock around each emit.&lt;/strong&gt; Same critique, more so. Network round-trip + lock-TTL failure mode (what if the worker crashes holding it?) for a thing your runtime already gives you tools for.&lt;/p&gt;




&lt;h2&gt;
  
  
  How — a single-consumer drainer
&lt;/h2&gt;

&lt;p&gt;The race only happens because &lt;em&gt;multiple coroutines&lt;/em&gt; call into the publish path concurrently. Restrict that to one, and the race vanishes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7m2z5873ujy0067js5yv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7m2z5873ujy0067js5yv.png" alt="drainer-pattern" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The principle works in any language:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Runtime&lt;/th&gt;
&lt;th&gt;Queue&lt;/th&gt;
&lt;th&gt;Single-consumer task&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Python&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.python.org/3/library/asyncio-queue.html" rel="noopener noreferrer"&gt;&lt;code&gt;asyncio.Queue&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;asyncio.create_task(drain(...))&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Go&lt;/td&gt;
&lt;td&gt;unbuffered or buffered &lt;code&gt;chan&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;go drain(ch)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Node&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://github.com/sindresorhus/p-queue" rel="noopener noreferrer"&gt;&lt;code&gt;p-queue&lt;/code&gt;&lt;/a&gt; with &lt;code&gt;concurrency: 1&lt;/code&gt;, or a hand-rolled async iterator&lt;/td&gt;
&lt;td&gt;a single consumer awaiting &lt;code&gt;for await (const ev of queue)&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rust&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.rs/tokio/latest/tokio/sync/mpsc/index.html" rel="noopener noreferrer"&gt;&lt;code&gt;tokio::sync::mpsc&lt;/code&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;tokio::spawn(drain(rx))&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Producers enqueue events; a single drainer dequeues them in FIFO order. &lt;strong&gt;The seq is generated &lt;em&gt;inside the drainer&lt;/em&gt;, after the dequeue, before the writes.&lt;/strong&gt; There's exactly one place in the program that touches the seq counter, so no race is possible.&lt;/p&gt;

&lt;p&gt;The Python implementation (real code from &lt;a href="https://github.com/AkshatSoni26/longshot/blob/main/app/publish.py" rel="noopener noreferrer"&gt;&lt;code&gt;app/publish.py&lt;/code&gt;&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# from app/publish.py — verbatim.
&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;__future__&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;annotations&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dataclasses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dataclass&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;redis.asyncio&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Redis&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;keys&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app.events&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TERMINAL_TYPES&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BaseEvent&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app.settings&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;get_settings&lt;/span&gt;


&lt;span class="nd"&gt;@dataclass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frozen&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;slots&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;_DrainerStop&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Typed sentinel: tells a session&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s drainer to flush and exit.

    Single instance below — comparing identity (``is _DRAINER_STOP``) costs
    nothing and lets the queue stay strictly typed as
    ``BaseEvent | _DrainerStop`` (no ``object``, no ``Any``).
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;


&lt;span class="n"&gt;_DRAINER_STOP&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;_DrainerStop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_DrainerStop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;_QueueItem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BaseEvent&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;_DrainerStop&lt;/span&gt;


&lt;span class="nd"&gt;@dataclass&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;_SessionPublisher&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Queue&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;_QueueItem&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;drainer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProgressPublisher&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;One instance per worker process. Holds a drainer per active session.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Redis&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_redis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_sessions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_SessionPublisher&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_lock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Lock&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_settings&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BaseEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Enqueue a typed event. ``event.session_id`` routes to the right drainer.

        ``event.seq`` is rewritten by the drainer just before the write — it&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s
        fine (and expected) to leave it at the ``0`` default when constructing.
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&gt;publisher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_get_or_create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;publisher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Drain remaining items, stop the drainer. Call after a terminal event.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&gt;publisher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_sessions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;publisher&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;publisher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_DRAINER_STOP&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;publisher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drainer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;TimeoutError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;publisher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_get_or_create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_SessionPublisher&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;session_id&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_sessions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_sessions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_lock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;session_id&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_sessions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_sessions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Queue&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;_QueueItem&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Queue&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;drainer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_drain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;drain:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_sessions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_SessionPublisher&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;drainer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;drainer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_sessions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Producers don't touch Redis. They put events on a queue. The drainer is the only consumer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_drain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Queue&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;_QueueItem&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;The single writer for this session. FIFO guarantees ordered seqs.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&gt;list_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;events_list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;snap_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;progress_snapshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;chan_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;seq_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sequence&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;ttl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_settings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;progress_snapshot_ttl_seconds&lt;/span&gt;

        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_DrainerStop&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt;

            &lt;span class="n"&gt;seq&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;incr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;seq_key&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model_copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;seq&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;seq&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
            &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model_dump_json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

            &lt;span class="c1"&gt;# One pipelined round-trip for the durable writes + channel publish.
&lt;/span&gt;            &lt;span class="n"&gt;pipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rpush&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;list_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expire&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;list_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ttl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;snap_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;ttl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chan_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;TERMINAL_TYPES&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="c1"&gt;# Best-effort: keep terminal events around longer than mid-stream
&lt;/span&gt;                &lt;span class="c1"&gt;# ones so a late SSE client still sees the outcome.
&lt;/span&gt;                &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expire&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;list_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ttl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read the loop carefully. Inside one iteration:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Pop one event from the queue.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;INCR&lt;/code&gt; the sequence counter.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;model_copy&lt;/code&gt; the event with the fresh seq (events are constructed by producers with &lt;code&gt;seq=0&lt;/code&gt; placeholder).&lt;/li&gt;
&lt;li&gt;Pipeline four writes in one round-trip.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The next event from the queue &lt;strong&gt;cannot&lt;/strong&gt; start step 2 until step 4 finishes. There is no other consumer of this queue. &lt;a href="https://docs.python.org/3/library/asyncio-queue.html" rel="noopener noreferrer"&gt;&lt;code&gt;asyncio.Queue&lt;/code&gt;&lt;/a&gt; is FIFO. &lt;strong&gt;Sequence numbers come out monotonic by construction.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;What's &lt;em&gt;not&lt;/em&gt; there:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No &lt;code&gt;transaction=True&lt;/code&gt; on the pipeline. The pipeline is purely a round-trip optimization — it batches the four commands into one network call. We don't need &lt;code&gt;MULTI&lt;/code&gt;/&lt;code&gt;EXEC&lt;/code&gt; for the &lt;strong&gt;ordering&lt;/strong&gt; guarantee, because the drainer is the only actor &lt;em&gt;writing&lt;/em&gt; this session's event list, snapshot, channel publish, and seq counter; ordering is enforced by the single-consumer property of the loop, not by Redis. (A non-transactional pipeline is &lt;em&gt;not&lt;/em&gt; atomic — if your product needs cross-key consistency, e.g. snapshot always agreeing with the latest list entry from any reader's perspective, you'd need a transaction or Lua script. We don't, so we don't.)&lt;/li&gt;
&lt;li&gt;No Redis-side script, no &lt;code&gt;WATCH&lt;/code&gt;/&lt;code&gt;MULTI&lt;/code&gt;/&lt;code&gt;EXEC&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;No distributed lock.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A small implementation detail worth flagging: each event resets the list's TTL with &lt;code&gt;pipe.expire(list_key, 3600)&lt;/code&gt;. That means the events list lives for 1 hour &lt;em&gt;after the last event&lt;/em&gt;, not 1 hour from creation — a rolling window. Active sessions stay around as long as they're producing; abandoned ones expire on schedule.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why a typed sentinel and not &lt;code&gt;None&lt;/code&gt; or &lt;code&gt;object()&lt;/code&gt;?&lt;/strong&gt; Because the queue type is &lt;code&gt;asyncio.Queue[BaseEvent | _DrainerStop]&lt;/code&gt;, fully type-checked. A bare &lt;code&gt;object()&lt;/code&gt; would type the queue as &lt;code&gt;Queue[object]&lt;/code&gt; and gut every guarantee on the producer side. Small thing; saves you a debugging session three months from now.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  How — typed events at the boundary
&lt;/h2&gt;

&lt;p&gt;One last piece. Every byte coming back through the SSE boundary is validated against a &lt;a href="https://docs.pydantic.dev/" rel="noopener noreferrer"&gt;Pydantic&lt;/a&gt; discriminated union:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;StartedEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseEvent&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Literal&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;EventType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;STARTED&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;EventType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;STARTED&lt;/span&gt;
    &lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TokenEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseEvent&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Literal&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;EventType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TOKEN&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;EventType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TOKEN&lt;/span&gt;
    &lt;span class="n"&gt;delta&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;

&lt;span class="c1"&gt;# ... one concrete subclass per event type ...
&lt;/span&gt;
&lt;span class="n"&gt;AnyEvent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Annotated&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;StartedEvent&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;TokenEvent&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;ChunkSummarizedEvent&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;...,&lt;/span&gt;
    &lt;span class="nc"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;discriminator&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;EVENT_ADAPTER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TypeAdapter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AnyEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Equivalents in other languages: &lt;a href="https://zod.dev/?id=discriminated-unions" rel="noopener noreferrer"&gt;Zod discriminated unions&lt;/a&gt; in TypeScript, &lt;a href="https://serde.rs/enum-representations.html#internally-tagged" rel="noopener noreferrer"&gt;&lt;code&gt;serde&lt;/code&gt; with &lt;code&gt;#[serde(tag = "type")]&lt;/code&gt;&lt;/a&gt; in Rust, sealed classes in Kotlin/Java. The principle: &lt;strong&gt;misspelled keys fail at the boundary, not three layers down.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The failure matrix
&lt;/h2&gt;

&lt;p&gt;The architecture above closes the loop on a lot of failure modes. Here's the full set, with where each one is caught and what the client actually sees:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Failure mode&lt;/th&gt;
&lt;th&gt;Where it's caught&lt;/th&gt;
&lt;th&gt;What the client sees&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Client closes tab mid-task&lt;/td&gt;
&lt;td&gt;Work is in worker, not request — keeps running&lt;/td&gt;
&lt;td&gt;Reconnect = full replay&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Network drops for 30s&lt;/td&gt;
&lt;td&gt;Same as above&lt;/td&gt;
&lt;td&gt;Reconnect = full replay&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Worker process crashes mid-task&lt;/td&gt;
&lt;td&gt;Stream pending-message claim (&lt;code&gt;XAUTOCLAIM&lt;/code&gt; after idle timeout) + idempotency lock&lt;/td&gt;
&lt;td&gt;No double work — but session doesn't auto-resume; client must resubmit (true resumption is in &lt;em&gt;What this isn't&lt;/em&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Same task delivered twice&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;SET NX EX&lt;/code&gt; lock at task entry&lt;/td&gt;
&lt;td&gt;Second delivery is a silent no-op&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Known pipeline error (&lt;code&gt;FetchError&lt;/code&gt;, &lt;code&gt;SummarizeError&lt;/code&gt; — e.g. LLM unreachable, HTTP fetch timeout)&lt;/td&gt;
&lt;td&gt;Inner pipeline emits &lt;code&gt;ErrorEvent&lt;/code&gt;; &lt;code&gt;run_job&lt;/code&gt; catches and returns &lt;code&gt;TaskResult(status=ERROR)&lt;/code&gt; — no broker retry&lt;/td&gt;
&lt;td&gt;SSE &lt;code&gt;error&lt;/code&gt; event, then close&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unknown exception&lt;/td&gt;
&lt;td&gt;Emits &lt;code&gt;ErrorEvent&lt;/code&gt;, then re-raises to the broker (retry middleware may redeliver; idempotency lock catches the dupe)&lt;/td&gt;
&lt;td&gt;SSE &lt;code&gt;error&lt;/code&gt; event, then close&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Task hangs forever&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;asyncio.wait_for&lt;/code&gt; hard timeout&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;error&lt;/code&gt; event with &lt;code&gt;reason=timeout&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User clicks Cancel&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;DELETE /jobs/{id}&lt;/code&gt; sets cancel flag&lt;/td&gt;
&lt;td&gt;Worker exits at next checkpoint, emits &lt;code&gt;cancelled&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Concurrent emits (the seq bug)&lt;/td&gt;
&lt;td&gt;Single-consumer drainer&lt;/td&gt;
&lt;td&gt;Strictly monotonic seqs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The interesting column is the third. &lt;strong&gt;Most worker-side failures become events the client can observe&lt;/strong&gt; — error, cancelled, timeout. The two that don't are by design: a client disconnect is just a generator exit (the worker keeps going, reconnect replays everything), and a duplicate delivery is a silent no-op (that's the whole point of the lock). No HTTP error mid-stream (SSE clients don't see those). No silent retries that rewrite history. The client either sees the truth or gets a clean termination — never a confused middle state.&lt;/p&gt;




&lt;h2&gt;
  
  
  What this isn't
&lt;/h2&gt;

&lt;p&gt;A few things this design explicitly does not solve, and shouldn't:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Resumption from a checkpoint.&lt;/strong&gt; "Worker crash → no double work" is &lt;em&gt;not&lt;/em&gt; the same as "task picks up where it left off." Real resumption means the task itself is structured around a state machine you can re-enter — a much larger design change. The idempotency lock just guarantees one task body executes per session, not that it executes correctly after partial progress.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-worker fan-out for a single session.&lt;/strong&gt; The drainer assumes one worker owns one session at a time. If multiple workers need to publish into the same session, you're back to needing a server-side primitive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Persistence beyond an hour.&lt;/strong&gt; The events list and snapshot are TTL'd. They're a streaming buffer, not your source of truth — keep your application's record in your database.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The stack at a glance
&lt;/h2&gt;

&lt;p&gt;The demo repo uses these tools, but &lt;strong&gt;none of them are essential to the pattern&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concern&lt;/th&gt;
&lt;th&gt;What I used&lt;/th&gt;
&lt;th&gt;Common alternatives&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Web framework&lt;/td&gt;
&lt;td&gt;&lt;a href="https://fastapi.tiangolo.com/" rel="noopener noreferrer"&gt;FastAPI&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Express, Axum, Gin, Spring&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Background queue&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://taskiq-python.github.io/" rel="noopener noreferrer"&gt;TaskIQ&lt;/a&gt; + &lt;a href="https://redis.io/docs/data-types/streams/" rel="noopener noreferrer"&gt;Redis Streams&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Celery, BullMQ, Sidekiq, RabbitMQ Streams, Kafka&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Live channel&lt;/td&gt;
&lt;td&gt;&lt;a href="https://redis.io/docs/manual/pubsub/" rel="noopener noreferrer"&gt;Redis Pub/Sub&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;NATS, Kafka, Postgres &lt;code&gt;LISTEN/NOTIFY&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Client transport&lt;/td&gt;
&lt;td&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events" rel="noopener noreferrer"&gt;Server-Sent Events&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;WebSockets, gRPC server streaming, long polling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schema&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://docs.pydantic.dev/" rel="noopener noreferrer"&gt;Pydantic&lt;/a&gt; discriminated unions&lt;/td&gt;
&lt;td&gt;Zod, serde, sealed classes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Local LLM (optional)&lt;/td&gt;
&lt;td&gt;&lt;a href="https://lmstudio.ai/" rel="noopener noreferrer"&gt;LM Studio&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Ollama, vLLM, llama.cpp&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The &lt;strong&gt;architecture&lt;/strong&gt; — separate the work from the connection, three storage shapes per event, replay-then-tail, idempotency at the task entry, single-consumer drainer for ordering — is the core idea. Stack swaps are mechanical.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;Full implementation on GitHub:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔗 &lt;strong&gt;Repo:&lt;/strong&gt; &lt;a href="https://github.com/AkshatSoni26/longshot" rel="noopener noreferrer"&gt;github.com/AkshatSoni26/longshot&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🔗 &lt;strong&gt;The drainer:&lt;/strong&gt; &lt;a href="https://github.com/AkshatSoni26/longshot/blob/main/app/publish.py" rel="noopener noreferrer"&gt;&lt;code&gt;app/publish.py&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🔗 &lt;strong&gt;The replay-then-tail SSE endpoint:&lt;/strong&gt; &lt;a href="https://github.com/AkshatSoni26/longshot/blob/main/app/api.py" rel="noopener noreferrer"&gt;&lt;code&gt;app/api.py&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🔗 &lt;strong&gt;Idempotency lock + cancel + timeout:&lt;/strong&gt; &lt;a href="https://github.com/AkshatSoni26/longshot/blob/main/app/tasks.py" rel="noopener noreferrer"&gt;&lt;code&gt;app/tasks.py&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/AkshatSoni26/longshot
&lt;span class="nb"&gt;cd &lt;/span&gt;longshot
make up                  &lt;span class="c"&gt;# docker compose: redis + api + worker&lt;/span&gt;
bash demo/happy_path.sh  &lt;span class="c"&gt;# POST a job, watch SSE events stream&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The repo includes three modes for the LLM call: a deterministic mock (no network), &lt;a href="https://www.anthropic.com/api" rel="noopener noreferrer"&gt;Anthropic&lt;/a&gt;, and &lt;a href="https://lmstudio.ai/" rel="noopener noreferrer"&gt;LM Studio&lt;/a&gt; (free, local, OpenAI-compatible). LM Studio mode handles thinking models like Qwen3-Thinking with &lt;code&gt;&amp;lt;think&amp;gt;&lt;/code&gt; blocks stripped &lt;em&gt;during&lt;/em&gt; the streaming pass.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I'd do differently next time
&lt;/h2&gt;

&lt;p&gt;Honest list:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Real integration tests against a Redis container.&lt;/strong&gt; The smoke tests verify wiring. They don't verify the drainer's invariants under contention, and they don't verify the SSE replay/dedupe path. Both are testable with &lt;code&gt;pytest-asyncio&lt;/code&gt; + the &lt;a href="https://testcontainers-python.readthedocs.io/" rel="noopener noreferrer"&gt;&lt;code&gt;testcontainers&lt;/code&gt;&lt;/a&gt; Redis module.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A property-based test on the drainer.&lt;/strong&gt; Spin up N producers emitting random events, assert the consumer sees strictly increasing seqs. The drainer makes this true by construction; a &lt;a href="https://hypothesis.works/" rel="noopener noreferrer"&gt;Hypothesis&lt;/a&gt; test would prove it under contention.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resumable tasks.&lt;/strong&gt; Real resumption is a state-machine rewrite — task body broken into idempotent steps, each step writing a checkpoint key, the lock check skipping completed steps on retry. Big design change, worth its own post.&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;em&gt;The pattern in this post comes from production code I built for a long-running AI-orchestration system, after we got tired of users losing five minutes of progress every time their VPN reconnected. The repo is a sanitized, dependency-light extraction — a reference implementation, not a copy of the real thing — designed to be small enough to read end-to-end. If you build something on top of it, &lt;a href="https://github.com/AkshatSoni26/longshot/issues" rel="noopener noreferrer"&gt;open an issue&lt;/a&gt; — I'd love to see what you do with it.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🤝 Sharing thoughts, open to corrections
&lt;/h2&gt;

&lt;p&gt;I'm a few years into my career — not the staff engineer who's seen every production failure mode. I'm sharing this because writing it down forced me to understand the design more carefully than building it did, and I'd rather have it reviewed in public than convince myself the demo is bulletproof.&lt;/p&gt;

&lt;p&gt;If something here is off, overclaimed, or naive in a way I'll cringe at in five years, &lt;strong&gt;please tell me.&lt;/strong&gt; Open an &lt;a href="https://github.com/AkshatSoni26/longshot/issues" rel="noopener noreferrer"&gt;issue on the repo&lt;/a&gt; or drop a comment below — corrections from people who've shipped this kind of system at scale are exactly what I want. The whole point of writing publicly is getting better, not being right. 🙌&lt;/p&gt;

</description>
      <category>backend</category>
      <category>redis</category>
      <category>ai</category>
      <category>python</category>
    </item>
    <item>
      <title>Contract-Based Design: How I Make AI Agents Work Faster Without Breaking Each Other</title>
      <dc:creator>Akshat Soni</dc:creator>
      <pubDate>Wed, 22 Apr 2026 15:55:08 +0000</pubDate>
      <link>https://forem.com/akshatsoni26/contract-based-design-how-i-make-ai-agents-work-faster-without-breaking-each-other-1jn2</link>
      <guid>https://forem.com/akshatsoni26/contract-based-design-how-i-make-ai-agents-work-faster-without-breaking-each-other-1jn2</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;These are my personal thoughts based on what I've been experimenting with in my day-to-day work. I'm not claiming this is the perfect way — I'm sharing what works for me and I'm genuinely open to hearing what you think, what I'm missing, and how you approach this differently.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Problem Nobody Talks About
&lt;/h2&gt;

&lt;p&gt;Everyone is excited about AI agents. You give them a task, they go build it. Sounds great.&lt;/p&gt;

&lt;p&gt;But here's what actually happens when you build something real — like a fullstack AI chatbot SaaS:&lt;/p&gt;

&lt;p&gt;You launch an agent to build the chat UI. Another to handle message history. Another for authentication. Another for the AI response backend.&lt;/p&gt;

&lt;p&gt;And then they start waiting on each other.&lt;/p&gt;

&lt;p&gt;The frontend agent pauses because it doesn't know what the API response looks like. The auth agent blocks the chat agent. The AI backend agent makes assumptions about the input format that don't match what the frontend sends.&lt;/p&gt;

&lt;p&gt;You end up with agents that are technically "parallel" but practically sequential. Half your time is spent fixing mismatches between what one agent produced and what another expected.&lt;/p&gt;


&lt;div class="crayons-card c-embed"&gt;

  &lt;br&gt;
&lt;strong&gt;The Root Problem:&lt;/strong&gt; Agents are coupled to &lt;strong&gt;each other&lt;/strong&gt; instead of coupled to &lt;strong&gt;data&lt;/strong&gt;. That's what kills parallelism in real projects.&lt;br&gt;

&lt;/div&gt;


&lt;p&gt;I've been thinking about this problem for a while. And I came up with a practice I call &lt;strong&gt;Contract-Based Design&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is Contract-Based Design?
&lt;/h2&gt;

&lt;p&gt;The idea is simple.&lt;/p&gt;

&lt;p&gt;Before any agent writes a single line of code, one agent does one job only: &lt;strong&gt;understand the full requirement and define the contracts.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A contract is a small &lt;code&gt;.md&lt;/code&gt; file that defines exactly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What goes &lt;strong&gt;in&lt;/strong&gt; (input)&lt;/li&gt;
&lt;li&gt;What comes &lt;strong&gt;out&lt;/strong&gt; (output)&lt;/li&gt;
&lt;li&gt;What the &lt;strong&gt;boundaries&lt;/strong&gt; of this piece are&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once every piece has a contract, every agent has everything it needs to work completely independently — without waiting, without assuming, without breaking each other.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Real Example: Building an AI Chatbot SaaS
&lt;/h2&gt;

&lt;p&gt;Let's say the task is: &lt;strong&gt;build a fullstack AI chatbot SaaS with user auth, chat UI, message history, AI responses, and user settings.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Without contracts, this is a mess. Every piece touches every other piece. Agents block each other constantly.&lt;/p&gt;

&lt;p&gt;With Contract-Based Design, here's what happens:&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 1 — The Contract Definition Phase
&lt;/h3&gt;

&lt;p&gt;Before launching any implementation agent, I first instruct one agent to do only this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Understand the full requirement. Divide all tasks. For tasks that can be independent, define them clearly. For tasks that depend on others, define the exact input and output so they can still run in parallel."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That agent produces a contracts folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/contracts
  auth.contract.md
  chat-ui.contract.md
  message-history.contract.md
  ai-response.contract.md
  user-settings.contract.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;
  Click to see example contract files
  &lt;p&gt;&lt;strong&gt;&lt;code&gt;auth.contract.md&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Auth Contract&lt;/span&gt;

INPUT:
&lt;span class="p"&gt;  -&lt;/span&gt; token: string (JWT)

OUTPUT:
&lt;span class="p"&gt;  -&lt;/span&gt; userId: string
&lt;span class="p"&gt;  -&lt;/span&gt; role: "admin" | "user"
&lt;span class="p"&gt;  -&lt;/span&gt; error?: string

BOUNDARIES:
&lt;span class="p"&gt;  -&lt;/span&gt; Does not handle session storage
&lt;span class="p"&gt;  -&lt;/span&gt; Does not communicate with chat or AI modules
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;&lt;code&gt;ai-response.contract.md&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## AI Response Contract&lt;/span&gt;

INPUT:
&lt;span class="p"&gt;  -&lt;/span&gt; context: Message[] (last 10 messages)
&lt;span class="p"&gt;  -&lt;/span&gt; prompt: string
&lt;span class="p"&gt;  -&lt;/span&gt; settings: { model: string, temperature: number }

OUTPUT:
&lt;span class="p"&gt;  -&lt;/span&gt; response: ReadableStream
&lt;span class="p"&gt;  -&lt;/span&gt; error?: string

BOUNDARIES:
&lt;span class="p"&gt;  -&lt;/span&gt; Does not fetch message history
&lt;span class="p"&gt;  -&lt;/span&gt; Does not handle auth
&lt;span class="p"&gt;  -&lt;/span&gt; Streams response, does not buffer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;&lt;code&gt;message-history.contract.md&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Message History Contract&lt;/span&gt;

INPUT:
&lt;span class="p"&gt;  -&lt;/span&gt; userId: string
&lt;span class="p"&gt;  -&lt;/span&gt; limit: number (default: 10)

OUTPUT:
&lt;span class="p"&gt;  -&lt;/span&gt; messages: { role: "user" | "assistant", content: string, timestamp: ISO8601 }[]
&lt;span class="p"&gt;  -&lt;/span&gt; error?: string

BOUNDARIES:
&lt;span class="p"&gt;  -&lt;/span&gt; Does not call AI
&lt;span class="p"&gt;  -&lt;/span&gt; Does not handle auth
&lt;span class="p"&gt;  -&lt;/span&gt; Read-only, does not write messages
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;/p&gt;
&lt;h3&gt;
  
  
  Phase 2 — Parallel Launch
&lt;/h3&gt;

&lt;p&gt;Once the contracts exist, I launch all agents simultaneously:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Agent&lt;/th&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Depends On&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Agent 1&lt;/td&gt;
&lt;td&gt;Build Chat UI component&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;chat-ui.contract.md&lt;/code&gt; only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent 2&lt;/td&gt;
&lt;td&gt;Build Message History API&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;message-history.contract.md&lt;/code&gt; only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent 3&lt;/td&gt;
&lt;td&gt;Build Auth middleware&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;auth.contract.md&lt;/code&gt; only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent 4&lt;/td&gt;
&lt;td&gt;Build AI Response backend&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ai-response.contract.md&lt;/code&gt; only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent 5&lt;/td&gt;
&lt;td&gt;Build User Settings page&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;user-settings.contract.md&lt;/code&gt; only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;None of them wait for each other. None of them make assumptions. They each read their contract and build exactly what it says.&lt;/p&gt;

&lt;p&gt;Agent 1 building the chat UI doesn't need Agent 4 to finish the AI backend — it already knows from the contract that the backend will return a &lt;code&gt;ReadableStream&lt;/code&gt;. It builds against that shape.&lt;/p&gt;


&lt;div class="crayons-card c-embed"&gt;

  &lt;br&gt;
&lt;strong&gt;The Shift:&lt;/strong&gt; From agent dependencies → to data dependencies. Agents don't need to know about each other. They only need to know their contract.&lt;br&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Why This Works
&lt;/h2&gt;

&lt;p&gt;The key insight is that you've separated &lt;strong&gt;thinking&lt;/strong&gt; from &lt;strong&gt;doing&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Most people launch agents and let them figure out interfaces as they go. This creates hidden dependencies and mismatches you only discover when things break.&lt;/p&gt;

&lt;p&gt;Contract-Based Design forces all interface decisions to be made &lt;strong&gt;before&lt;/strong&gt; any implementation starts.&lt;/p&gt;

&lt;p&gt;This is exactly how senior engineers design systems before delegating to a team. You don't tell five developers to go build five things and figure out how they connect later. You design the interfaces first, then everyone builds in parallel.&lt;/p&gt;

&lt;p&gt;Contract-Based Design makes AI agents work the same way.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Three Concepts I Use Together
&lt;/h2&gt;

&lt;p&gt;Contract-Based Design doesn't live alone. I combine it with two other practices:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. PRD.md Files&lt;/strong&gt;&lt;br&gt;
Before contracts, there's a product requirements document in markdown. This is the high-level "what are we building and why." The contract-definition agent reads the PRD and produces the contracts from it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Spec-Based Design&lt;/strong&gt;&lt;br&gt;
Each agent gets a spec — not just a contract. The spec includes the contract plus implementation notes, tech stack constraints, and edge cases. The contract defines the interface. The spec defines the behavior.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Contract-Based Design&lt;/strong&gt;&lt;br&gt;
The contracts are the source of truth for all inter-agent communication. No agent makes assumptions. If it's not in the contract, it doesn't exist.&lt;/p&gt;

&lt;p&gt;Together these three create a system where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The PRD tells agents &lt;strong&gt;what to build&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;The spec tells agents &lt;strong&gt;how to build it&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;The contract tells agents &lt;strong&gt;what to expect and produce&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  One More Thing: The Validation Step
&lt;/h2&gt;

&lt;p&gt;After all agents finish, I run one final agent whose only job is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Check that every component's actual output matches its contract. Flag any drift."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is the safety net. It catches mismatches before they hit production. And because the contracts are the source of truth, the validation is objective — either the output matches the contract or it doesn't.&lt;/p&gt;




&lt;h2&gt;
  
  
  What This Unlocks
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Without Contracts&lt;/th&gt;
&lt;th&gt;With Contracts&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Execution&lt;/td&gt;
&lt;td&gt;Sequential blocking&lt;/td&gt;
&lt;td&gt;Truly parallel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interfaces&lt;/td&gt;
&lt;td&gt;Discovered during build&lt;/td&gt;
&lt;td&gt;Agreed upfront&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Debugging&lt;/td&gt;
&lt;td&gt;Painful — where did it break?&lt;/td&gt;
&lt;td&gt;Easy — contract shows exactly where&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scaling&lt;/td&gt;
&lt;td&gt;Adding agents breaks things&lt;/td&gt;
&lt;td&gt;Just add a new contract file&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;You produce more. You compromise nothing.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thought
&lt;/h2&gt;

&lt;p&gt;AI agents are not slow because the models are slow.&lt;/p&gt;

&lt;p&gt;They are slow because the &lt;em&gt;system&lt;/em&gt; around them is not designed for parallelism.&lt;/p&gt;

&lt;p&gt;Contract-Based Design is about building that system — so your agents can do what they're actually capable of: working fast, working independently, and producing real output without stepping on each other.&lt;/p&gt;

&lt;p&gt;I've been using this in my day-to-day work and it has genuinely changed how I think about building with AI. But I want to be clear — &lt;strong&gt;this is just my current thinking.&lt;/strong&gt; I'm still learning, still experimenting, and I'm sure there are better ways to do parts of this.&lt;/p&gt;

&lt;p&gt;If you've tried something similar, or you think I'm wrong about something, or you have a better approach — &lt;strong&gt;I really want to hear it.&lt;/strong&gt; Drop a comment. Let's figure this out together.&lt;/p&gt;

&lt;p&gt;That's the whole point of sharing this. 🙂&lt;/p&gt;




&lt;p&gt;&lt;em&gt;sharing thoughts, not rules.&lt;/em&gt;&lt;/p&gt;




</description>
      <category>ai</category>
      <category>architecture</category>
      <category>webdev</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Understanding Array Mutation in Redux: A Common Pitfall with useSelector</title>
      <dc:creator>Akshat Soni</dc:creator>
      <pubDate>Mon, 09 Sep 2024 17:19:06 +0000</pubDate>
      <link>https://forem.com/akshatsoni26/understanding-array-mutation-in-redux-a-common-pitfall-with-useselector-1ibo</link>
      <guid>https://forem.com/akshatsoni26/understanding-array-mutation-in-redux-a-common-pitfall-with-useselector-1ibo</guid>
      <description>&lt;h3&gt;
  
  
  What is the Problem?
&lt;/h3&gt;

&lt;p&gt;When working with Redux, we often deal with immutable data structures to maintain the predictability of the application state. However, recently I encounter issue is unintentionally mutating the state, especially when handling arrays. &lt;/p&gt;

&lt;p&gt;let's try, A common scenario is trying to reorder an array from the Redux store and inadvertently modifying the original array. &lt;/p&gt;

&lt;p&gt;In this blog, we’ll explore this issue and how &lt;code&gt;useSelector&lt;/code&gt; in Redux gives us a reference to the original state, which can lead to unexpected side effects.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Does This Happen?
&lt;/h3&gt;

&lt;p&gt;The root cause of this problem stems from how JavaScript handles arrays and objects. When we use &lt;code&gt;useSelector&lt;/code&gt; in Redux, it doesn’t return a new copy of the state. It returns a reference to the current state. If we modify this array using JavaScript methods like &lt;code&gt;sort()&lt;/code&gt;, &lt;code&gt;splice()&lt;/code&gt;, or &lt;code&gt;reverse()&lt;/code&gt;, these methods mutate the original array in place, affecting the state inside your Redux store.&lt;/p&gt;

&lt;p&gt;Here's an example of what happens:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Selector to get an array from the Redux store&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myArray&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useSelector&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;myArray&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Attempting to reorder the array&lt;/span&gt;
&lt;span class="nx"&gt;myArray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// This mutates the original array&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, the &lt;code&gt;sort()&lt;/code&gt; function modifies &lt;code&gt;myArray&lt;/code&gt; directly, meaning it alters both the local variable and the array in the Redux store because &lt;code&gt;myArray&lt;/code&gt; is just a reference to the store’s state.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the Best Process?
&lt;/h3&gt;

&lt;p&gt;To avoid mutating the original array in Redux, we need to create a new copy of the array before making any changes. This ensures that the Redux store remains immutable, which is crucial for features like time travel debugging, state predictability, and preventing unwanted side effects.&lt;/p&gt;

&lt;p&gt;Here’s how we can handle it correctly:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Create a Shallow Copy of the Array&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Use methods like &lt;code&gt;slice()&lt;/code&gt;, &lt;code&gt;[...spread operator]&lt;/code&gt;, or &lt;code&gt;Array.from()&lt;/code&gt; to create a shallow copy of the array before modifying it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Work on the New Copy&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Modify the copied array without affecting the original.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Example of the Correct Approach:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Selector to get an array from the Redux store&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myArray&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useSelector&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;myArray&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Create a copy of the array before modifying&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newArray&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;myArray&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;  &lt;span class="c1"&gt;// Or use myArray.slice() / Array.from(myArray)&lt;/span&gt;

&lt;span class="c1"&gt;// Modify the new array&lt;/span&gt;
&lt;span class="nx"&gt;newArray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Use newArray without affecting the original Redux state&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By using the spread operator or other cloning methods, we're ensuring that we work with a new instance of the array rather than mutating the one inside the Redux store.&lt;/p&gt;

&lt;h3&gt;
  
  
  Best Practices for Handling Redux State
&lt;/h3&gt;

&lt;p&gt;To summarize, here are some best practices to avoid mutating the Redux state:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Never directly modify state obtained via &lt;code&gt;useSelector&lt;/code&gt;.&lt;/strong&gt; Always create a new copy before applying any changes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prefer pure functions&lt;/strong&gt; like &lt;code&gt;map()&lt;/code&gt;, &lt;code&gt;filter()&lt;/code&gt;, and &lt;code&gt;reduce()&lt;/code&gt; to modify arrays instead of mutating methods like &lt;code&gt;sort()&lt;/code&gt; or &lt;code&gt;splice()&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Understanding how JavaScript works with references and Redux’s immutability principles is crucial to managing our state effectively. Always ensure that we create new instances of arrays or objects before modifying them to avoid unintentional state mutations. By following these best practices, we can write more predictable and bug-free Redux applications.&lt;/p&gt;

&lt;p&gt;if you have any suggestions or questions let me know in comment.&lt;/p&gt;

</description>
      <category>redux</category>
      <category>react</category>
      <category>learning</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Understanding the Key Differences Between | and || in TypeScript</title>
      <dc:creator>Akshat Soni</dc:creator>
      <pubDate>Sun, 25 Aug 2024 07:36:05 +0000</pubDate>
      <link>https://forem.com/akshatsoni26/understanding-the-key-differences-between-and-in-typescript-6jd</link>
      <guid>https://forem.com/akshatsoni26/understanding-the-key-differences-between-and-in-typescript-6jd</guid>
      <description>&lt;p&gt;This blog post will show the difference between these two operators.&lt;/p&gt;

&lt;p&gt;In TypeScript, operators are essential tools that allow us to manipulate data and control the flow of applications. Among the various operators, the pipe symbol (&lt;code&gt;|&lt;/code&gt;) and the double pipe symbol (&lt;code&gt;||&lt;/code&gt;) are often used, but they serve very different purposes. &lt;/p&gt;

&lt;p&gt;Understanding the difference between these two operators is crucial for writing efficient and error-free code.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;What is this?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The single pipe (&lt;code&gt;|&lt;/code&gt;) in TypeScript is known as the &lt;strong&gt;union type operator&lt;/strong&gt;. It allows a variable to hold one of several types, essentially broadening the &lt;strong&gt;range of acceptable values&lt;/strong&gt; for that variable. &lt;/p&gt;

&lt;p&gt;On the other hand, the double pipe (&lt;code&gt;||&lt;/code&gt;) is the &lt;strong&gt;logical OR operator&lt;/strong&gt;, used to evaluate expressions and return the &lt;strong&gt;first truthy&lt;/strong&gt; value encountered, or the &lt;strong&gt;last value if all are falsy&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Why is this?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The union type (&lt;code&gt;|&lt;/code&gt;) is vital for situations where a variable can legitimately represent multiple types, offering flexibility while maintaining type safety. &lt;/p&gt;

&lt;p&gt;The logical OR operator (&lt;code&gt;||&lt;/code&gt;), however, is often used in control flow to set default values or short-circuit evaluations, making it a powerful tool in conditional statements.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Use Case&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Union Type (&lt;code&gt;|&lt;/code&gt;):&lt;/strong&gt; Suppose we're dealing with a function that can return a string or a number based on certain conditions. Using the union type allows us to define a return type that accommodates both possibilities.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`ID: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Logical OR (&lt;code&gt;||&lt;/code&gt;):&lt;/strong&gt; We might want to assign a default value to a variable if it’s undefined or null. The logical OR operator helps us to do that efficiently.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;inputUsername&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Guest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Pros and Cons&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Union Type (&lt;code&gt;|&lt;/code&gt;):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Pros:&lt;/em&gt; &lt;/li&gt;
&lt;li&gt;Enhances flexibility and type safety.&lt;/li&gt;
&lt;li&gt;Reduces the need for extensive type checks.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Logical OR (&lt;code&gt;||&lt;/code&gt;):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Pros:&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Simplifies conditional logic.&lt;/li&gt;
&lt;li&gt;Provides a quick way to set default values.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Cons:&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;May lead to unintended consequences if falsy values are legitimate (e.g., &lt;code&gt;0&lt;/code&gt;, &lt;code&gt;''&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Overuse can reduce code clarity.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Both the union type (&lt;code&gt;|&lt;/code&gt;) and the logical OR (&lt;code&gt;||&lt;/code&gt;) operators are powerful tools in TypeScript, each with its specific use cases and benefits. &lt;/p&gt;

&lt;p&gt;if you have any suggestions or questions let me know in comment.&lt;/p&gt;

</description>
      <category>learning</category>
      <category>typescript</category>
      <category>programming</category>
      <category>javascript</category>
    </item>
    <item>
      <title>[empty * n] Syntax in JavaScript</title>
      <dc:creator>Akshat Soni</dc:creator>
      <pubDate>Sun, 18 Aug 2024 10:46:33 +0000</pubDate>
      <link>https://forem.com/akshatsoni26/empty-n-syntax-in-javascript-59ia</link>
      <guid>https://forem.com/akshatsoni26/empty-n-syntax-in-javascript-59ia</guid>
      <description>&lt;p&gt;In JavaScript, we often encounter various syntaxes and constructs that may seem unusual at first glance. One such construct is the &lt;code&gt;[empty * n]&lt;/code&gt; syntax. &lt;/p&gt;

&lt;p&gt;In This blog will explore what this syntax means, its purpose, why it's useful, its pros and cons, and a concluding thought on its use.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the &lt;code&gt;[empty * n]&lt;/code&gt; Syntax in JavaScript?
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;[empty * n]&lt;/code&gt; syntax is a shorthand way to describe an array of &lt;code&gt;n&lt;/code&gt; empty slots. In essence, it represents an array where each element is an empty slot, not &lt;code&gt;undefined&lt;/code&gt; or &lt;code&gt;null&lt;/code&gt;, but &lt;strong&gt;truly empty&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;emptyArray&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emptyArray&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// [empty × 5]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, the &lt;code&gt;emptyArray&lt;/code&gt; is created using the &lt;code&gt;Array&lt;/code&gt; constructor with the argument &lt;code&gt;5&lt;/code&gt;. The output &lt;code&gt;[empty × 5]&lt;/code&gt; signifies that the array has five empty slots.&lt;/p&gt;

&lt;h2&gt;
  
  
  Purpose of the &lt;code&gt;[empty * n]&lt;/code&gt; Syntax
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;[empty * n]&lt;/code&gt; syntax is primarily used to create arrays of a specified length without initializing the elements. This is particularly useful in situations where you want to reserve space for an array but plan to populate it later or need to perform operations where the indices are more important than the values.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use Cases:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Initializing Arrays for Further Processing:&lt;/strong&gt;&lt;br&gt;
We might want to create an array of a certain size and fill it with values at a later point in our code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Mapping Operations:&lt;/strong&gt;&lt;br&gt;
Using &lt;code&gt;.map()&lt;/code&gt;, &lt;code&gt;.fill()&lt;/code&gt;, or other array methods to populate the array after creation.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Creating Placeholder Arrays:&lt;/strong&gt;&lt;br&gt;
When working with large datasets, this syntax allows us to create an array of placeholders that can be efficiently filled later.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Why is it Useful?
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;[empty * n]&lt;/code&gt; syntax can be a powerful tool in certain scenarios:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Performance:&lt;/strong&gt; Creating an array with empty slots is faster than creating one with initialized values like &lt;code&gt;undefined&lt;/code&gt; or &lt;code&gt;null&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexibility:&lt;/strong&gt; It allows developers to create a structure that can be manipulated later, offering greater flexibility in how arrays are utilized in the code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory Efficiency:&lt;/strong&gt; Since the elements are not initialized, it may lead to reduced memory usage in certain contexts.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Pros and Cons
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Pros:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Quick Initialization:&lt;/strong&gt;&lt;br&gt;
Creating an array with a fixed length is quick and doesn't require specifying initial values.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Efficiency in Large Arrays:&lt;/strong&gt;&lt;br&gt;
When working with large arrays, this method can be more efficient in terms of both performance and memory usage.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flexible Array Manipulation:&lt;/strong&gt;&lt;br&gt;
We can easily populate or modify the array using various methods such as &lt;code&gt;.fill()&lt;/code&gt;, &lt;code&gt;.map()&lt;/code&gt;, or even traditional loops.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Cons:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Lack of Readability:&lt;/strong&gt;&lt;br&gt;
The &lt;code&gt;[empty * n]&lt;/code&gt; syntax can be confusing for those unfamiliar with it, making the code less readable.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Potential for Errors:&lt;/strong&gt;&lt;br&gt;
Since the slots are truly empty, certain array methods (e.g., &lt;code&gt;.forEach()&lt;/code&gt;, &lt;code&gt;.map()&lt;/code&gt;) may not behave as expected, leading to potential bugs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Limited Use Cases:&lt;/strong&gt;&lt;br&gt;
This approach is not always suitable, particularly when specific initial values are required or when the array size needs to be dynamic.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;[empty * n]&lt;/code&gt; syntax in JavaScript is a useful construct for us who need to create arrays of a fixed length without initializing the elements. While it offers benefits in terms of performance and flexibility, it can also introduce challenges related to readability and potential errors. Understanding when and how to use this syntax can help us write more efficient and effective JavaScript code.&lt;/p&gt;

&lt;p&gt;In this blog, If you have any doubt or suggestions.Please let me know in comments.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>learning</category>
      <category>buildinpublic</category>
    </item>
    <item>
      <title>Preventing Data Leaks in React.js: A Deep Dive into useEffect</title>
      <dc:creator>Akshat Soni</dc:creator>
      <pubDate>Sun, 11 Aug 2024 11:47:41 +0000</pubDate>
      <link>https://forem.com/akshatsoni26/preventing-data-leaks-in-reactjs-a-deep-dive-into-useeffect-4b2g</link>
      <guid>https://forem.com/akshatsoni26/preventing-data-leaks-in-reactjs-a-deep-dive-into-useeffect-4b2g</guid>
      <description>&lt;p&gt;in this blog, I am writing about some data leaking problem and what is problem with that.&lt;/p&gt;

&lt;p&gt;React.js is a powerful library for building user interfaces, but like any tool, it requires careful handling to avoid potential pitfalls. One such pitfall is &lt;strong&gt;data leaking&lt;/strong&gt;, which can lead to performance issues and unexpected behavior in our applications. &lt;br&gt;
Today, we're going to explore this concept, using a code example to illustrate both the problem and its solution.&lt;/p&gt;
&lt;h2&gt;
  
  
  Understanding Data Leaks in React
&lt;/h2&gt;

&lt;p&gt;Data leaks in React often occur when &lt;strong&gt;components don't properly clean up&lt;/strong&gt; after themselves. This is particularly common when working with side effects, such as setting up subscriptions or timers. &lt;br&gt;
If these aren't properly managed, they can &lt;strong&gt;continue to run even after a component has unmounted&lt;/strong&gt;, and this leading to memory leaks and potential errors.&lt;/p&gt;

&lt;p&gt;Let's look at an example that demonstrates this issue:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;interval is running.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;20000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Analyzing the Code&lt;/p&gt;

&lt;p&gt;This useEffect hook is setting up interval and mount it to DOM.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;While this code might seem fine at first glance, there's a subtle issue that could lead to a data leak. The problem lies here that when this component unmount then it does not clear interval. And if we give some dependency in dependency array then it rerender at all time but it does not clearing pervious interval so which we are doing in interval it happened to multiple time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;To fix this issue, we need to ensure that we clear the previous interval before setting up a new one. Here's how we can modify the code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;interval&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;interval is running.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;20000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nf"&gt;clearInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;interval&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this updated version we first clear the interval which mount and then do new calculation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Best Practices for Preventing Data Leaks
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Always provide a cleanup function in useEffect when setting up subscriptions or timers.&lt;/li&gt;
&lt;li&gt;Be mindful of your effect's dependencies. Make sure all variables used in the effect are included in the dependency array.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Data leaks in React can be subtle and hard to spot, but they can have significant impacts on our application's performance. By understanding how effects work and following best practices, we can write more robust and efficient React applications. Always remember to clean up after your effects, and be mindful of when and why they're running.&lt;/p&gt;

&lt;p&gt;If any doubt regarding this let me know.&lt;/p&gt;

</description>
      <category>react</category>
      <category>learning</category>
      <category>webdev</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>Decoding JavaScript: Mastering Null, Undefined, and Empty Values</title>
      <dc:creator>Akshat Soni</dc:creator>
      <pubDate>Sun, 04 Aug 2024 09:10:04 +0000</pubDate>
      <link>https://forem.com/akshatsoni26/decoding-javascript-mastering-null-undefined-and-empty-values-hld</link>
      <guid>https://forem.com/akshatsoni26/decoding-javascript-mastering-null-undefined-and-empty-values-hld</guid>
      <description>&lt;p&gt;As we know, JavaScript is a dynamically typed language, which can sometimes confuse us when dealing with empty or non-existent values. In this blog post, we'll explore the differences between &lt;code&gt;null&lt;/code&gt;, &lt;code&gt;undefined&lt;/code&gt;, empty strings, and empty arrays in JavaScript, with code examples to illustrate each concept.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Null
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;null&lt;/code&gt; is a deliberate non-value. It represents a variable that has been explicitly defined as having &lt;strong&gt;no value&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;myVariable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myVariable&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Output: null&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;myVariable&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Output: "object"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;code&gt;typeof null&lt;/code&gt; returns &lt;code&gt;object&lt;/code&gt;, which is a &lt;strong&gt;known quirk in JavaScript due to legacy reasons&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Undefined
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;undefined&lt;/code&gt; represents a variable that has been declared but hasn't been assigned a value yet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;myUndefinedVariable&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;myUndefinedVariable&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Output: undefined&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;myUndefinedVariable&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Output: "undefined"&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;myFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;param&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;param&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nf"&gt;myFunction&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Output: undefined&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Empty String ('')
&lt;/h2&gt;

&lt;p&gt;An empty string is a valid string with a length of zero.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;emptyString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emptyString&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Output: ""&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;emptyString&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Output: "string"&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emptyString&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Output: 0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Empty Array ([])
&lt;/h2&gt;

&lt;p&gt;An empty array is a list with no elements.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;emptyArray&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emptyArray&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Output: []&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;emptyArray&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Output: "object"&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emptyArray&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// Output: true&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emptyArray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Output: 0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Comparison and Use Cases
&lt;/h2&gt;

&lt;p&gt;Let's compare these different types:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Output: true&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Output: false&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Output: false&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Output: false&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Output: false&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Output: false&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// Output: false&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// Output: false&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// Output: false&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;([]));&lt;/span&gt; &lt;span class="c1"&gt;// Output: true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Checking for null or undefined
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;isNullOrUndefined&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isNullOrUndefined&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// Output: true&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isNullOrUndefined&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// Output: true&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isNullOrUndefined&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// Output: false&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isNullOrUndefined&lt;/span&gt;&lt;span class="p"&gt;([]));&lt;/span&gt; &lt;span class="c1"&gt;// Output: false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Handling empty strings and arrays
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;isEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// Output: true&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isEmpty&lt;/span&gt;&lt;span class="p"&gt;([]));&lt;/span&gt; &lt;span class="c1"&gt;// Output: true&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isEmpty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hello&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// Output: false&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isEmpty&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]));&lt;/span&gt; &lt;span class="c1"&gt;// Output: false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  6. Best Practices
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Use &lt;code&gt;null&lt;/code&gt; when you want to indicate that a variable has no value explicitly.&lt;/li&gt;
&lt;li&gt;Let variables be &lt;code&gt;undefined&lt;/code&gt; when they're not assigned a value.&lt;/li&gt;
&lt;li&gt;Use empty strings ('') when you need a string with no characters.&lt;/li&gt;
&lt;li&gt;Use empty arrays ([]) when you need a list with no elements.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Always use strict equality (===) unless you have a specific reason not to.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;When checking for null or undefined, you can use &lt;code&gt;value == null&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Understanding the differences between &lt;code&gt;null&lt;/code&gt;, &lt;code&gt;undefined&lt;/code&gt;, empty strings, and empty arrays is crucial for writing clean and bug-free JavaScript code. Each has its use cases and behaves differently in comparisons and type checks. By using these values correctly and knowing their nuances, you can write more robust and maintainable JavaScript applications.&lt;/p&gt;

&lt;p&gt;Remember to always consider the context of your application when deciding which of these to use, and be consistent in your approach throughout your codebase.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>learning</category>
    </item>
    <item>
      <title>The Hidden Dangers of Shallow Copying in JavaScript: Lessons from API Response Handling</title>
      <dc:creator>Akshat Soni</dc:creator>
      <pubDate>Sun, 28 Jul 2024 08:36:58 +0000</pubDate>
      <link>https://forem.com/akshatsoni26/the-hidden-dangers-of-the-hidden-dangers-of-shallow-copying-in-javascript-lessons-from-api-response-handling-1cp1</link>
      <guid>https://forem.com/akshatsoni26/the-hidden-dangers-of-the-hidden-dangers-of-shallow-copying-in-javascript-lessons-from-api-response-handling-1cp1</guid>
      <description>&lt;h1&gt;
  
  
  Understanding Shallow vs Deep Copying in JavaScript: A Next.js API Example
&lt;/h1&gt;

&lt;p&gt;One common pitfall I encounter is the difference between shallow and deep copying of objects, especially when dealing with nested data structures. This blog post will explore this issue through a real-world example involving an API response in a Next.js project.&lt;/p&gt;

&lt;p&gt;Let's imagine that you have a default API response object defined as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;defaultApiResponse&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ApiResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;No data found&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, you create an API endpoint that uses this default response as a template for building actual responses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../../../database/prismaClient&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defaultApiResponse&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../ApiResponseType&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;defaultApiResponse&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;searchParams&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;product_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;product_id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;product_id&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nf"&gt;isNaN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;product_id&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;apiResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Product ID is required and must be a number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;apiResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;apiResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;apiResponse&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findUnique&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;product_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;apiResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Product not found&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;apiResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;apiResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;apiResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;apiResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;apiResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;apiResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Product found&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;apiResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;apiResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;apiResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;An unknown error occurred&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;apiResponse&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Initially, this code seems correct. However, upon making several requests, you notice that the &lt;code&gt;apiResponse&lt;/code&gt; object retains data from previous requests.&lt;/p&gt;

&lt;p&gt;Why is this happening? 🤔&lt;/p&gt;

&lt;h2&gt;
  
  
  Due to Shallow Copy
&lt;/h2&gt;

&lt;p&gt;But wait, what is Shallow copy? 🤔&lt;/p&gt;

&lt;h3&gt;
  
  
  Understanding Shallow Copy
&lt;/h3&gt;

&lt;p&gt;The line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;defaultApiResponse&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;uses the spread operator to create a new object. This is a shallow copy, meaning it only copies the top-level properties.&lt;/p&gt;

&lt;p&gt;If any of these properties are objects or arrays (like &lt;code&gt;data&lt;/code&gt; and &lt;code&gt;errors&lt;/code&gt;), the new object will reference the same memory locations as the original.&lt;/p&gt;

&lt;p&gt;Thus, modifying &lt;code&gt;apiResponse.errors&lt;/code&gt; or &lt;code&gt;apiResponse.data&lt;/code&gt; will also modify &lt;code&gt;defaultApiResponse.errors&lt;/code&gt; and &lt;code&gt;defaultApiResponse.data&lt;/code&gt;, leading to a persistent state across requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: Deep Copy
&lt;/h2&gt;

&lt;p&gt;To prevent this issue, you need to ensure that each request gets a completely independent copy of the &lt;code&gt;defaultApiResponse&lt;/code&gt;, including its nested arrays and objects.&lt;/p&gt;

&lt;p&gt;One way to achieve this is by using a utility function to create a fresh copy:&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Create a utility function
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// utils/apiResponse.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ApiResponse&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../ApiResponseType&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getDefaultApiResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;ApiResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
    &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;No data found&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
    &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Use the utility function in your API handler
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/server&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../../../database/prismaClient&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getDefaultApiResponse&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../../../utils/apiResponse&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getDefaultApiResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;searchParams&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;product_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;searchParams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;product_id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;product_id&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nf"&gt;isNaN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;product_id&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;apiResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Product ID is required and must be a number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;apiResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;apiResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;apiResponse&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;prisma&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findUnique&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;product_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;apiResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Product not found&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;apiResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;apiResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;apiResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;apiResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;apiResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;apiResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Product found&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;apiResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;apiResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;apiResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;An unknown error occurred&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;apiResponse&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you need to think about Why This Works. 🤔&lt;/p&gt;

&lt;p&gt;So to answer this:&lt;/p&gt;

&lt;p&gt;The utility function &lt;code&gt;getDefaultApiResponse&lt;/code&gt; ensures that every request handler invocation starts with a fresh, deep copy of the default response. This prevents shared references and ensures that modifications to &lt;code&gt;apiResponse&lt;/code&gt; do not affect &lt;code&gt;defaultApiResponse&lt;/code&gt; or other instances of &lt;code&gt;apiResponse&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Understanding the difference between shallow and deep copying in JavaScript is crucial when dealing with objects and arrays. Shallow copies can lead to unintended side effects, especially in stateful applications like APIs.&lt;/p&gt;

&lt;p&gt;By using utility functions to create fresh copies of default objects, you can avoid these pitfalls and ensure your code remains clean and maintainable.&lt;/p&gt;

&lt;p&gt;Thanks for reading my post. If you have any questions regarding this, let me know.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>javascript</category>
      <category>tutorial</category>
      <category>learning</category>
    </item>
  </channel>
</rss>
