<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged state-management</title>
<link>https://coderlegion.com/tag/state-management</link>
<description>Powered by Question2Answer</description>
<item>
<title>I am Jack's `&lt;engine&gt;`</title>
<link>https://coderlegion.com/27290/i-am-jacks-engine</link>
<description>&lt;p&gt;I am Jack's &lt;code&gt;&amp;lt;engine&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Without me, Jack's application has values without a place.&lt;/p&gt;
&lt;p&gt;Jack declares me once. I am a singleton. Every part of his program that reads my cell is&lt;br&gt;
reading the same me.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scrml&quot;&gt;${
    type Order:enum = { Cart, Paying, Paid, Shipped, Refunded }
}

&amp;lt;engine for=Order initial=.Cart&amp;gt;
    &amp;lt;Cart    rule=.Paying          : &quot;Your cart.&quot;&amp;gt;
    &amp;lt;Paying  rule=(.Paid | .Cart)  : &quot;Talking to the bank.&quot;&amp;gt;
    &amp;lt;Paid    rule=.Shipped         : &quot;Thank you.&quot;&amp;gt;
    &amp;lt;Shipped rule=.Refunded        : &quot;On its way.&quot;&amp;gt;
    &amp;lt;Refunded                      : &quot;We are sorry.&quot;&amp;gt;
&amp;lt;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That is all of me. Jack's order, the message, and the arrows.&lt;/p&gt;
&lt;p&gt;I declare Jack's variable for him. He never writes &lt;code&gt;@order&lt;/code&gt;. He receives it.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Every variant has a line. That is not tidiness. It is the deal.&lt;/p&gt;
&lt;p&gt;Jack has not built the refund screen yet. He deletes &lt;code&gt;&amp;lt;Refunded&amp;gt;&lt;/code&gt; from my body.&lt;/p&gt;
&lt;p&gt;He does not delete it from &lt;code&gt;Order&lt;/code&gt;. He removed the room, not the guest.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;code&gt;error [E-ENGINE-STATE-CHILD-MISSING]: &amp;lt;engine for=Order&amp;gt; body is missing a state-child for variant .Refunded.&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Somebody is going to get a refund.&lt;/p&gt;
&lt;p&gt;I do not know who. I know there will be a place for them to stand.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;The &lt;code&gt;rule=&lt;/code&gt; is the part Jack underestimates. It is not documentation. It is a contract on&lt;br&gt;
writes.&lt;/p&gt;
&lt;p&gt;Jack writes this at 2am:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scrml&quot;&gt;&amp;lt;Cart rule=.Paying&amp;gt;
    &amp;lt;button onclick=${ @order = .Shipped }&amp;gt;Check out&amp;lt;/&amp;gt;
&amp;lt;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;He is inside &lt;code&gt;&amp;lt;Cart&amp;gt;&lt;/code&gt;. I know precisely where he is standing. So I do not wait for a test,&lt;br&gt;
or a user, or a chargeback:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;code&gt;error [E-ENGINE-INVALID-TRANSITION]: @order = .Shipped inside state-child &amp;lt;Cart&amp;gt; is invalid. &amp;lt;Cart&amp;gt;'s rule=.Paying — only .Paying is reachable.&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Jack shipped an order that was never paid for. He shipped it in a text editor, to nobody,&lt;br&gt;
and I was the only one who saw.&lt;/p&gt;
&lt;p&gt;That is the whole incident. No page, no ticket, no customer.&lt;/p&gt;
&lt;p&gt;Jack closes the error and goes to bed.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Before me, Jack wrote this.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;const [isPaying,   setIsPaying]   = useState(false);
const [isPaid,     setIsPaid]     = useState(false);
const [isShipped,  setIsShipped]  = useState(false);
const [isRefunded, setIsRefunded] = useState(false);

// four hundred lines later
setIsShipped(true);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Four booleans. Sixteen combinations. Five of them are an order.&lt;/p&gt;
&lt;p&gt;The other eleven are paid and refunded and never shipped. Shipped and still sitting in the&lt;br&gt;
cart. They are exactly as easy to write as the real five, and nothing tells Jack which is&lt;br&gt;
which. He carries that list in his head, in every file.&lt;/p&gt;
&lt;p&gt;I do not refuse the other eleven. There is no way to say them.&lt;/p&gt;
&lt;p&gt;Jack could reach for a reducer. A reducer gives Jack his one variable back. It does not give&lt;br&gt;
him the arrows. It will take a &lt;code&gt;SHIP&lt;/code&gt; action while the order is still in the cart, hand back&lt;br&gt;
an order that is shipped, and type-check on the way out.&lt;/p&gt;
&lt;p&gt;I am Jack's raging bile duct!&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Jack has two ways to move. Both of them go through me.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scrml&quot;&gt;@order = .Paying            // a write. checked, and quiet when it holds.
@order.advance(.Paying)     // an assertion. this must hold, or halt.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The difference is not whether I check. It is what Jack means by failing. A write that&lt;br&gt;
cannot happen is a bug. An assertion that cannot happen is a belief Jack held that was&lt;br&gt;
wrong, and he wants the noise.&lt;/p&gt;
&lt;p&gt;There is no &lt;code&gt;.tryAdvance&lt;/code&gt;. One was proposed and refused, on the grounds that silent failures&lt;br&gt;
hide bugs.&lt;/p&gt;
&lt;p&gt;Writing Jack's current state back onto itself does nothing at all. Re-asserting where you&lt;br&gt;
are is not travel.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;I am not Jack's validation. Validation arrives after Jack has already built the wrong thing.&lt;/p&gt;
&lt;p&gt;I am the shape of what Jack is permitted to build.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/27290/i-am-jacks-engine</guid>
<pubDate>Tue, 15 Sep 2026 13:22:17 +0000</pubDate>
</item>
<item>
<title>Unreliable by Design: Building a Fetch Queue for a Flaky Internet</title>
<link>https://coderlegion.com/23679/unreliable-by-design-building-a-fetch-queue-for-a-flaky-internet</link>
<description>&lt;hr&gt;
&lt;p&gt;I want to talk about a problem that sounds simple until it isn't: fetching content from URLs that you don't control.&lt;/p&gt;
&lt;p&gt;You build a system that ingests web pages. It works great in development, you point it at a handful of reliable URLs and everything comes back clean. Then you deploy it. Users start throwing real-world links at it: a research paper hosted on a university server that throttles at 2 AM, a company blog that returns 503 for thirty seconds after a CDN hiccup, an API endpoint that drops the TCP connection halfway through a large response. Suddenly your &quot;working&quot; system is silently losing jobs.&lt;/p&gt;
&lt;p&gt;This is the story of how we thought through that problem, what we built, and what it taught us about resilient job queues.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-problem&quot;&gt;The Problem&lt;/h3&gt;
&lt;p&gt;The surface-level issue is easy to describe: sometimes HTTP requests fail. But the real challenge is more nuanced than that. There are at least three distinct failure modes that look superficially similar but demand completely different responses:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Permanent failures.&lt;/strong&gt; A 404 means the page is gone. A 410 means it was deliberately removed. Retrying these is pointless, you'll just get the same answer a thousand times.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Transient failures.&lt;/strong&gt; A 503 means the server is temporarily overloaded. A &lt;code&gt;ReadTimeout&lt;/code&gt; means the server started responding but stalled. A &lt;code&gt;RemoteProtocolError&lt;/code&gt; means the connection dropped before the response completed. These failures &lt;em&gt;do&lt;/em&gt; resolve on their own, the key word being &quot;on their own,&quot; not &quot;immediately.&quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Quota and rate-limit failures.&lt;/strong&gt; A 429 means you're sending requests too fast. Retrying immediately makes this actively worse. You need to back off and give the server room to breathe.&lt;/p&gt;
&lt;p&gt;When we first encountered this in production, our naive approach was: fail the job, surface an error, let the user retry manually. That's honest, but it's also terrible UX. A user who ingests twenty URLs shouldn't have to babysit the queue because three of them hit a momentary server hiccup at 3 AM.&lt;/p&gt;
&lt;p&gt;The fix needed to be automatic, and it needed to be &lt;em&gt;smart&lt;/em&gt; about the difference between &quot;this will never work&quot; and &quot;this will probably work if you wait a bit.&quot;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;classifying-errors-before-retrying&quot;&gt;Classifying Errors Before Retrying&lt;/h3&gt;
&lt;p&gt;The first piece of the solution was error classification. Not all exceptions are equal, and the retry logic should only apply to genuinely transient conditions. We settled on a concrete list:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Transient (retry with backoff):&lt;/strong&gt; connection timeouts, read timeouts, pool timeouts, connection refused, mid-stream read failures, protocol errors (TCP connection dropped), and HTTP 5xx responses&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fatal (fail immediately):&lt;/strong&gt; HTTP 4xx responses (except 429), DNS resolution failures for clearly invalid hosts, malformed URLs, parsing errors in the fetched content&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The distinction matters. If you retry a 404, you're wasting retry budget on something that will never change. If you &lt;em&gt;don't&lt;/em&gt; retry a &lt;code&gt;ConnectTimeout&lt;/code&gt;, you're turning a 30-second server hiccup into a permanently failed job.&lt;/p&gt;
&lt;p&gt;One thing that surprised us: &lt;code&gt;httpx.RemoteProtocolError&lt;/code&gt; — the exception Python's &lt;code&gt;httpx&lt;/code&gt; raises when a server closes the connection mid-response — was not in our initial transient list. It was classified as a generic error, which meant it triggered a full stacktrace in the server log and didn't retry. In production, this happened constantly with certain CDN-backed sites that aggressively close idle connections. Getting the classification right was more important than the backoff math.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;why-just-retry-isn-t-enough&quot;&gt;Why &quot;Just Retry&quot; Isn't Enough&lt;/h3&gt;
&lt;p&gt;Once you know a failure is transient, the next question is: when do you retry?&lt;/p&gt;
&lt;p&gt;The instinct is to retry immediately, or after a fixed short delay. Both are wrong for the same reason: if a server is struggling, hitting it again right away just adds to its load. You become part of the problem.&lt;/p&gt;
&lt;p&gt;Exponential backoff is the standard solution, and for good reason. The idea is simple: each retry waits longer than the last, growing exponentially up to some cap. In our case:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;delay = min(base × 2^(attempt − 1), max_cap)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With a 30-second base and a 300-second cap, the schedule looks like:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Attempt    &lt;/th&gt;&lt;th&gt;Wait before retry&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;1st retry  &lt;/td&gt;&lt;td&gt;30 s&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2nd retry  &lt;/td&gt;&lt;td&gt;60 s&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;3rd retry  &lt;/td&gt;&lt;td&gt;120 s&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;4th+ retry &lt;/td&gt;&lt;td&gt;300 s (capped)&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;This gives a struggling server time to recover. It also means that a brief hiccup (a server that was down for 45 seconds) resolves on the second attempt rather than burning through all three retries in thirty seconds.&lt;/p&gt;
&lt;p&gt;We deliberately left out jitter in this implementation. Jitter — adding random noise to the delay — is valuable when you have many clients hitting the same endpoint simultaneously (it desynchronises their retry waves). With a single-instance queue serving one user's ingestion jobs, the coordination problem doesn't exist yet. We'll revisit this when we get to multi-user support.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-architecture-backoff-without-blocking&quot;&gt;The Architecture: Backoff Without Blocking&lt;/h3&gt;
&lt;p&gt;Here's where it gets interesting. The obvious implementation of &quot;wait before retrying&quot; is to sleep: put the worker thread to sleep for the backoff duration, then try again. This is simple but has a critical flaw. While the worker is sleeping on one job, &lt;em&gt;all other jobs&lt;/em&gt; are also blocked.&lt;/p&gt;
&lt;p&gt;The solution we used is a pattern worth knowing: &lt;strong&gt;store the retry timestamp in the job record, not in the worker&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;When a job fails with a transient error, instead of sleeping, we:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Compute the backoff delay&lt;/li&gt;
&lt;li&gt;Write &lt;code&gt;retry_after = datetime('now', '+30 seconds')&lt;/code&gt; into the job's database record&lt;/li&gt;
&lt;li&gt;Set the job status back to &lt;code&gt;pending&lt;/code&gt; and return immediately&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The worker loop doesn't sleep at all. It keeps running, calling &lt;code&gt;dequeue()&lt;/code&gt; to pick up the next available job. The &lt;code&gt;dequeue()&lt;/code&gt; query just filters out jobs whose &lt;code&gt;retry_after&lt;/code&gt; timestamp hasn't passed yet:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sql&quot;&gt;SELECT ... FROM jobs
WHERE status = 'pending'
  AND (retry_after IS NULL OR retry_after &amp;lt;= datetime('now'))
ORDER BY created_at ASC
LIMIT 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The result: a job waiting out its 60-second backoff window doesn't block any other job. Other URLs in the queue get processed normally during that window. When the window expires, the job becomes eligible again and gets picked up on the next &lt;code&gt;dequeue()&lt;/code&gt; call.&lt;/p&gt;
&lt;p&gt;Here's the full state flow:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=12127754559831910927&quot; alt=&quot;state flow&quot;&gt;&lt;/p&gt;
&lt;p&gt;One subtlety worth noting: when the last retry attempt fails (the job goes &lt;code&gt;dead&lt;/code&gt;), the log message should say so clearly. Our initial implementation always logged &quot;will retry&quot; regardless of whether the job was actually going to retry or die. We fixed this by reading the current retry count from the database &lt;em&gt;before&lt;/em&gt; calling &lt;code&gt;fail()&lt;/code&gt;, so the message accurately reflects what's about to happen. Operational clarity in logs is underrated, it's the difference between an engineer immediately knowing what happened and spending twenty minutes tracing through job IDs.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;this-is-a-common-pattern&quot;&gt;This Is a Common Pattern&lt;/h3&gt;
&lt;p&gt;What we built here isn't novel. It's a simplified version of what every serious distributed task queue does. Amazon SQS has visibility timeouts and dead-letter queues. Celery has &lt;code&gt;max_retries&lt;/code&gt; and &lt;code&gt;countdown&lt;/code&gt;. Sidekiq (Ruby) does exponential backoff with jitter by default.&lt;/p&gt;
&lt;p&gt;The pattern has a name: &lt;strong&gt;at-least-once delivery with bounded retries&lt;/strong&gt;. The key properties are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A job is retried &lt;em&gt;at least&lt;/em&gt; as many times as configured (transient failures don't count as &quot;the job failed&quot; until the budget is exhausted)&lt;/li&gt;
&lt;li&gt;Each retry is bounded by a delay that grows, so you don't cause thundering-herd problems&lt;/li&gt;
&lt;li&gt;The retry window is non-blocking, other work proceeds during the wait&lt;/li&gt;
&lt;li&gt;Dead jobs are surfaced explicitly for manual inspection or operator retry&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What's often underappreciated is &lt;em&gt;why&lt;/em&gt; you store the retry timestamp in the database rather than sleeping the worker. It's not just about throughput. It's about durability: if the server restarts while a job is waiting out its backoff window, the retry schedule survives. The timestamp is in persistent storage; the sleep would not be.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;what-changes-with-multiple-concurrent-users&quot;&gt;What Changes with Multiple Concurrent Users&lt;/h3&gt;
&lt;p&gt;Everything described above works well for a single-user instance with one job queue. When you move to multi-tenant or high-concurrency scenarios, the picture changes in interesting ways.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fairness becomes a problem.&lt;/strong&gt; If user A submits 100 URLs right before user B submits 5, user B's jobs sit at the back of the queue. The current FIFO ordering has no concept of per-user fairness. A weighted round-robin or per-tenant sub-queue would fix this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Per-domain rate limiting becomes important.&lt;/strong&gt; Right now, backoff is per-job. If ten different jobs all target the same domain and it starts returning 503, each one independently backs off and retries. What you actually want is &lt;em&gt;domain-level&lt;/em&gt; rate limiting: once you've seen a 503 from &lt;code&gt;example.com&lt;/code&gt;, all pending jobs targeting &lt;code&gt;example.com&lt;/code&gt; should pause, not just the one that just failed. This requires a shared rate-limit state (either in the database or in a cache) that all workers can read.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Jitter earns its place.&lt;/strong&gt; With multiple workers and multiple users, you &lt;em&gt;will&lt;/em&gt; get synchronized retry waves. Adding &lt;code&gt;± 20%&lt;/code&gt; jitter to the backoff delay breaks this up. With a single worker it's noise; with ten workers it's meaningful.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The worker concurrency model matters.&lt;/strong&gt; Our current design is a single async worker loop. Scaling to multiple workers introduces the classic dequeue race: two workers might pick up the same job if the status update and the dequeue aren't atomic. The standard fix is a &lt;code&gt;SELECT ... FOR UPDATE&lt;/code&gt; or optimistic locking pattern on the job row. SQLite doesn't support &lt;code&gt;SELECT FOR UPDATE&lt;/code&gt;, but the write-ahead-log mode plus careful transaction scoping can achieve the same guarantee.&lt;/p&gt;
&lt;p&gt;None of these are insurmountable, they're well-understood problems with well-understood solutions. But it's worth being deliberate about them before they appear in production rather than after.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;what-we-learned&quot;&gt;What We Learned&lt;/h3&gt;
&lt;p&gt;A few things stuck with us from going through this:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Error classification is the hard part.&lt;/strong&gt; The math of exponential backoff is trivial. Figuring out &lt;em&gt;which&lt;/em&gt; exceptions deserve a retry versus an immediate failure took more thought than everything else combined. The naive instinct is &quot;retry everything,&quot; which leads to wasting retry budgets on permanent failures. The opposite instinct — &quot;fail fast on anything unexpected&quot; — turns transient network noise into user-visible failures. The answer is a maintained allowlist of exception types that are genuinely transient.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Log accuracy is operational infrastructure.&lt;/strong&gt; A log message that says &quot;will retry&quot; when the job is actually dying is worse than no message at all, it sends operators down the wrong path. Writing accurate log messages requires reading state at the moment of logging, not assuming what state will be. It's a small discipline with a large payoff.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Sleeping the worker is the wrong abstraction.&lt;/strong&gt; The first intuition for &quot;wait before retrying&quot; is almost always to sleep. Don't. Store the timestamp, return immediately, and let the scheduler do its job. The sleep-based approach is brittle (server restarts lose the sleep), non-composable (you can't introspect what a sleeping worker is waiting for), and blocking (everything queues up behind it). The timestamp-in-database approach is none of these things.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Design for observability from the start.&lt;/strong&gt; &lt;code&gt;synthadoc jobs list&lt;/code&gt; showing job status, retry count, and &lt;code&gt;retry_after&lt;/code&gt; timestamp turned out to be more valuable than anticipated. When something goes wrong, being able to see exactly which jobs are in which state — and why — is the fastest path to resolution. Bolting observability on after the fact is always more expensive than building it in.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;The specific implementation here is for a LLM domain knowledgebase compilation engine, but the underlying patterns — transient/fatal error classification, timestamp-based backoff, non-blocking retry windows, accurate operational logging — apply anywhere you're calling external services over an unreliable network. Which is to say, anywhere.&lt;/p&gt;
&lt;p&gt;If you're building something similar and have questions, the discussion is open.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/23679/unreliable-by-design-building-a-fetch-queue-for-a-flaky-internet</guid>
<pubDate>Tue, 28 Jul 2026 15:37:44 +0000</pubDate>
</item>
<item>
<title>Optimistic UI: Unmasking the Elusive Race Conditions Hiding in Plain Sight</title>
<link>https://coderlegion.com/23205/optimistic-ui-unmasking-the-elusive-race-conditions-hiding-in-plain-sight</link>
<description>&lt;h3 id=&quot;the-allure-and-peril-of-optimistic-uis-when-instant-feedback-becomes-a-race-against-time&quot;&gt;The Allure and Peril of Optimistic UIs: When Instant Feedback Becomes a Race Against Time&lt;/h3&gt;
&lt;p&gt;In the relentless pursuit of a seamless user experience, modern web applications constantly strive for speed and responsiveness. One of the most powerful techniques to achieve this is the &lt;strong&gt;Optimistic UI&lt;/strong&gt;. Instead of making users wait for a server response before updating the interface, an optimistic UI presumes the server operation will succeed and updates the UI immediately. The result? A buttery-smooth, instant interaction that delights users and makes your application feel incredibly fast.&lt;/p&gt;
&lt;p&gt;Imagine clicking a &quot;Like&quot; button, adding an item to a cart, or toggling a setting. With an optimistic UI, the button instantly changes, the item appears in your cart, or the setting flips, all &lt;em&gt;before&lt;/em&gt; the actual network request has even completed. It's a fantastic illusion, masking network latency and making your application feel incredibly snappy.&lt;/p&gt;
&lt;p&gt;However, this magic comes with a significant caveat: what happens when the server operation &lt;em&gt;doesn't&lt;/em&gt; succeed? Or, more insidiously, what happens when multiple optimistic operations occur in rapid succession, leading to a clash of expectations between the client and the server? This is where &lt;strong&gt;race conditions&lt;/strong&gt; rear their ugly head, often manifesting as elusive, hard-to-reproduce bugs that only appear under specific, often stressful, conditions – like the &quot;fifth click&quot; scenario that inspired this deep dive.&lt;/p&gt;
&lt;p&gt;These &quot;fifth click&quot; bugs are the stuff of developer nightmares. They're intermittent, defy simple reproduction steps, and can erode user trust. They highlight the delicate balance between perceived performance and data integrity, forcing us to confront the complexities of asynchronous state management in distributed systems. This article will explore the mechanics of optimistic UIs, dissect how race conditions emerge, provide concrete code examples, and lay out robust strategies to prevent and debug these subtle yet critical issues.&lt;/p&gt;
&lt;h3 id=&quot;understanding-optimistic-ui-a-double-edged-sword&quot;&gt;Understanding Optimistic UI: A Double-Edged Sword&lt;/h3&gt;
&lt;p&gt;At its core, an optimistic UI is a gamble. It optimistically assumes success for an asynchronous operation and reflects that success in the user interface immediately. If the server eventually confirms success, no further UI changes are needed. If the server responds with an error, the UI must gracefully revert to its previous state, or inform the user of the failure.&lt;/p&gt;
&lt;h4 id=&quot;how-it-works&quot;&gt;How It Works&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;User Action:&lt;/strong&gt; The user performs an action (e.g., clicks &quot;Like&quot;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Optimistic UI Update:&lt;/strong&gt; The UI immediately updates to reflect the &lt;em&gt;expected&lt;/em&gt; outcome (e.g., the &quot;Like&quot; count increments, the button changes color).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;API Call:&lt;/strong&gt; Simultaneously, a network request is sent to the backend to perform the actual operation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Server Response:&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Success:&lt;/strong&gt; The server confirms the operation. The UI, already reflecting the correct state, often does nothing further or simply confirms the success silently.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Failure:&lt;/strong&gt; The server responds with an error. The UI must then &quot;rollback&quot; to its state before the optimistic update, and typically inform the user of the failure.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;the-benefits-why-we-love-it&quot;&gt;The Benefits: Why We Love It&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Improved Perceived Performance:&lt;/strong&gt; The most significant advantage. Users don't experience the lag of network round trips.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Smoother User Experience:&lt;/strong&gt; Interactions feel instant and fluid, reducing frustration, especially on slow networks.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Enhanced Engagement:&lt;/strong&gt; A responsive interface encourages more interaction.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;common-use-cases&quot;&gt;Common Use Cases&lt;/h4&gt;
&lt;p&gt;Optimistic UIs are particularly effective for actions that are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Frequent and Low-Impact:&lt;/strong&gt; Liking posts, starring items, following users.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Idempotent:&lt;/strong&gt; Repeating the action multiple times has the same effect as doing it once (though this isn't a strict requirement, it simplifies error handling).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Visually Obvious:&lt;/strong&gt; The user sees an immediate change that can be easily reverted if needed.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;the-anatomy-of-a-race-condition-in-optimistic-uis&quot;&gt;The Anatomy of a Race Condition in Optimistic UIs&lt;/h3&gt;
&lt;p&gt;A race condition occurs when the correctness of a program depends on the relative timing or interleaving of multiple operations. In an optimistic UI, this typically happens when:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Multiple asynchronous operations are initiated close together.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Their responses arrive out of order or after subsequent UI updates.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The UI's state becomes inconsistent with the true server state, or with the expected sequence of events.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The &quot;fifth click&quot; phenomenon perfectly illustrates the insidious nature of these bugs. It's not about a simple one-off error; it's about a sequence of events, network delays, and potentially transient server issues that combine to break the application's state in a way that's incredibly difficult to predict or reproduce.&lt;/p&gt;
&lt;h4 id=&quot;why-optimistic-uis-are-prone-to-race-conditions&quot;&gt;Why Optimistic UIs are Prone to Race Conditions&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Asynchronous Nature:&lt;/strong&gt; Network requests are inherently asynchronous. We send them off and wait for a response, but we don't control &lt;em&gt;when&lt;/em&gt; that response arrives.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Client-Side State vs. Server-Side Truth:&lt;/strong&gt; The optimistic UI creates a temporary divergence between the client's perceived state and the server's actual state. Managing this divergence, especially with multiple concurrent operations, is complex.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;User Behavior:&lt;/strong&gt; Users are unpredictable. They click rapidly, navigate quickly, and often don't wait for visual cues before initiating new actions.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;specific-scenarios-leading-to-fifth-click-bugs&quot;&gt;Specific Scenarios Leading to &quot;Fifth Click&quot; Bugs&lt;/h4&gt;
&lt;p&gt;Let's explore some concrete examples of how race conditions can manifest:&lt;/p&gt;
&lt;h5 id=&quot;scenario-1-rapid-fire-toggles-or-likes&quot;&gt;Scenario 1: Rapid-Fire Toggles or Likes&lt;/h5&gt;
&lt;p&gt;Imagine a &quot;Like&quot; button. The user clicks it, the UI optimistically increments the like count and changes the button's style. Before the server responds to the first click, the user clicks it &lt;em&gt;again&lt;/em&gt; (perhaps thinking the first click didn't register due to a brief visual stutter, or just being impatient).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Problem:&lt;/strong&gt; The second click's optimistic update might be based on the &lt;em&gt;first&lt;/em&gt; optimistic state (e.g., &lt;code&gt;likes + 1&lt;/code&gt;), not the confirmed server state. If the first request then fails and rolls back, the second request's optimistic update might still be active, leading to an incorrect &lt;code&gt;likes&lt;/code&gt; count (e.g., &lt;code&gt;original + 1&lt;/code&gt; instead of &lt;code&gt;original - 1&lt;/code&gt; if the first failed and the second succeeded). Or, if the server responses arrive out of order, a successful response from the first click might overwrite a subsequent optimistic rollback from a failed second click.&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&quot;scenario-2-dependent-operations-with-optimistic-deletes&quot;&gt;Scenario 2: Dependent Operations with Optimistic Deletes&lt;/h5&gt;
&lt;p&gt;A user optimistically deletes an item from a list. The item visually disappears. Before the server confirms the deletion, the user quickly performs another action that relies on the list's contents, such as filtering, sorting, or adding a &lt;em&gt;new&lt;/em&gt; item that might depend on the previous item's absence.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Problem:&lt;/strong&gt; If the deletion fails, the item reappears. But what if the subsequent filter/sort operation was already based on the &lt;em&gt;optimistically deleted&lt;/em&gt; state? Or what if a new item was added, but its position in the list was calculated assuming the deleted item was gone? The UI could show a list that's a mix of actual server data and stale optimistic data.&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&quot;scenario-3-pagination-or-filtering-with-asynchronous-updates&quot;&gt;Scenario 3: Pagination or Filtering with Asynchronous Updates&lt;/h5&gt;
&lt;p&gt;A user is viewing a paginated list of items. They click &quot;next page,&quot; and the UI optimistically loads the next page's data. Meanwhile, an item on the &lt;em&gt;previous&lt;/em&gt; page is being updated or deleted by another user (or a background process). When the server finally responds for the &quot;next page&quot; request, it might fetch data based on a slightly different global state than what the user optimistically expected, leading to missing items or duplicates across pages.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Problem:&lt;/strong&gt; If the optimistic &quot;next page&quot; state is based on a stale item count, the actual items returned by the server might not match the expected number or order, causing visual glitches or incorrect data display.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These scenarios highlight that the challenge isn't just about handling a single success or failure, but managing a continuous stream of potential state changes from both the client and the server, often out of sync.&lt;/p&gt;
&lt;h3 id=&quot;code-examples-illustrating-the-problem&quot;&gt;Code Examples: Illustrating the Problem&lt;/h3&gt;
&lt;p&gt;Let's look at a simplified React component that demonstrates a vulnerable optimistic UI pattern.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import React from 'react';

// Simulate an API call with random delays and occasional failures
const fakeApiCall = (newValue) =&amp;gt; new Promise((resolve, reject) =&amp;gt; {
  const delay = Math.random() * 800 + 200; // 200ms to 1s delay
  setTimeout(() =&amp;gt; {
    if (Math.random() &amp;lt; 0.3) { // 30% chance of failure for demonstration
      reject(new Error(&quot;Network error or server issue&quot;));
    } else {
      resolve({ success: true, newState: newValue });
    }
  }, delay);
});

function OptimisticToggleVulnerable() {
  const [isOn, setIsOn] = React.useState(false);
  const [isLoading, setIsLoading] = React.useState(false);
  const [error, setError] = React.useState(null);

  const handleToggle = async () =&amp;gt; {
    if (isLoading) {
      // This prevents multiple *API requests* from being sent concurrently
      // but doesn't fully protect against race conditions on UI state.
      console.log(&quot;Already loading, ignoring click.&quot;);
      return;
    }

    const previousState = isOn; // Capture current state for potential rollback
    setIsOn(!isOn); // Optimistic UI update
    setIsLoading(true);
    setError(null);

    try {
      console.log(`Sending API call for: ${!previousState}`);
      await fakeApiCall(!previousState); // Simulate API call
      console.log(`API call successful for: ${!previousState}`);
      // UI is already updated, nothing more to do on success here.
      // BUT, if another click happened, the UI might be in a different state now.
    } catch (err) {
      console.error(&quot;Toggle failed:&quot;, err.message);
      setIsOn(previousState); // Rollback to previous state
      setError(&quot;Failed to update. Please try again.&quot;);
    } finally {
      setIsLoading(false);
    }
  };

  return (
    &amp;lt;div style={{ padding: '20px', border: '1px solid #ccc', borderRadius: '8px' }}&amp;gt;
      &amp;lt;h3&amp;gt;Vulnerable Optimistic Toggle&amp;lt;/h3&amp;gt;
      &amp;lt;p&amp;gt;Status: &amp;lt;strong style={{ color: isOn ? 'green' : 'red' }}&amp;gt;{isOn ? &quot;On&quot; : &quot;Off&quot;}&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
      &amp;lt;button onClick={handleToggle} disabled={isLoading}&amp;gt;
        {isLoading ? &quot;Updating...&quot; : &quot;Toggle State&quot;}
      &amp;lt;/button&amp;gt;
      {error &amp;amp;&amp;amp; &amp;lt;p style={{ color: 'red', marginTop: '10px' }}&amp;gt;{error}&amp;lt;/p&amp;gt;}
      &amp;lt;p style={{ fontSize: '0.8em', color: '#666', marginTop: '15px' }}&amp;gt;
        Try clicking rapidly multiple times, especially when 'Updating...' is shown.
        Observe how the state might become inconsistent or flicker.
      &amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

// Render this component in your React app to test
// ReactDOM.render(&amp;lt;OptimisticToggleVulnerable /&amp;gt;, document.getElementById('root'));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;The Vulnerability:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In &lt;code&gt;OptimisticToggleVulnerable&lt;/code&gt;, if a user clicks rapidly:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The first click sets &lt;code&gt;isOn&lt;/code&gt; optimistically to &lt;code&gt;true&lt;/code&gt; and &lt;code&gt;isLoading&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A second click (while &lt;code&gt;isLoading&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt;) is ignored due to the &lt;code&gt;if (isLoading) return;&lt;/code&gt; guard. This prevents &lt;em&gt;multiple requests&lt;/em&gt;, but the UI is still in an optimistic state based on the &lt;em&gt;first&lt;/em&gt; click.&lt;/li&gt;
&lt;li&gt;Now, imagine the &lt;code&gt;fakeApiCall&lt;/code&gt; for the first click takes a long time and then &lt;em&gt;fails&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;catch&lt;/code&gt; block rolls back &lt;code&gt;setIsOn(previousState)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;What if the user had clicked a &lt;em&gt;third&lt;/em&gt; time very quickly, and that third click &lt;em&gt;wasn't&lt;/em&gt; ignored (because the &lt;code&gt;isLoading&lt;/code&gt; for the first request had just finished, but the UI was still &quot;on&quot; from the optimistic update)? The logic would get tangled.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This simple example shows that merely preventing concurrent &lt;em&gt;requests&lt;/em&gt; isn't enough. The core problem lies in managing the client-side optimistic state against the eventual server-side truth, especially when multiple user interactions might be &quot;in flight&quot; from the UI's perspective.&lt;/p&gt;
&lt;h3 id=&quot;strategies-for-taming-the-optimistic-beast-and-its-race-conditions-&quot;&gt;Strategies for Taming the Optimistic Beast (and its Race Conditions)&lt;/h3&gt;
&lt;p&gt;Mitigating race conditions in optimistic UIs requires a multi-faceted approach, combining careful client-side state management with robust server-side design.&lt;/p&gt;
&lt;h4 id=&quot;1-request-queuing-debouncing-and-throttling&quot;&gt;1. Request Queuing, Debouncing, and Throttling&lt;/h4&gt;
&lt;p&gt;For certain actions, especially those that are idempotent or where only the &lt;em&gt;latest&lt;/em&gt; user intent matters, you can limit the rate of requests.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Debouncing:&lt;/strong&gt; Ensures a function is only called after a certain period of inactivity. Useful for search inputs or resize events.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Throttling:&lt;/strong&gt; Limits how many times a function can be called over a period. Useful for scroll events or rapid button clicks where you only care about a few updates.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Request Queuing:&lt;/strong&gt; Instead of ignoring subsequent clicks, queue them up and process them sequentially, or discard older requests if a newer one supersedes them.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Trade-offs:&lt;/strong&gt; Can slightly reduce perceived responsiveness if overused, as it introduces a small delay before the optimistic update.&lt;/p&gt;
&lt;h4 id=&quot;2-state-management-with-transaction-ids-or-etags&quot;&gt;2. State Management with Transaction IDs or ETags&lt;/h4&gt;
&lt;p&gt;This is a more robust approach for ensuring consistency.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Concept:&lt;/strong&gt; Each optimistic update is given a unique identifier (a client-generated &lt;code&gt;transactionId&lt;/code&gt; or a server-provided &lt;code&gt;ETag&lt;/code&gt;/&lt;code&gt;version&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mechanism:&lt;/strong&gt;&lt;ol&gt;
&lt;li&gt;When an optimistic update is made, store its &lt;code&gt;transactionId&lt;/code&gt; along with the optimistic state.&lt;/li&gt;
&lt;li&gt;When the server response arrives, it should include the &lt;code&gt;transactionId&lt;/code&gt; of the request it's responding to.&lt;/li&gt;
&lt;li&gt;Compare the &lt;code&gt;transactionId&lt;/code&gt; in the response with the &lt;code&gt;transactionId&lt;/code&gt; of the &lt;em&gt;currently active&lt;/em&gt; optimistic state. If they don't match, it means a newer optimistic update has occurred, and the current response is stale. You might discard the stale response or use it to reconcile the state carefully.&lt;/li&gt;
&lt;li&gt;For updates, the server might return an &lt;code&gt;ETag&lt;/code&gt; or version number. The client can send this &lt;code&gt;ETag&lt;/code&gt; with subsequent updates (e.g., &lt;code&gt;If-Match&lt;/code&gt; header). If the &lt;code&gt;ETag&lt;/code&gt; doesn't match on the server, it means the server's state has changed, and the client's optimistic update is based on stale data.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Example (Conceptual):&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// Client-side state
const [pendingUpdates, setPendingUpdates] = useState([]); // Array of { transactionId, optimisticValue }

const handleToggle = async () =&amp;gt; {
  const transactionId = Math.random().toString(36).substring(2, 9); // Unique ID
  const previousState = isOn;

  setPendingUpdates(prev =&amp;gt; [...prev, { transactionId, optimisticValue: !previousState }]);
  setIsOn(!previousState); // Optimistic UI

  try {
    const response = await fakeApiCall(!previousState, transactionId); // Send ID to server
    // On success, remove from pendingUpdates matching transactionId
    setPendingUpdates(prev =&amp;gt; prev.filter(upd =&amp;gt; upd.transactionId !== response.transactionId));
  } catch (err) {
    // On error, remove from pendingUpdates and revert UI
    setPendingUpdates(prev =&amp;gt; prev.filter(upd =&amp;gt; upd.transactionId !== transactionId));
    // Rollback logic needs to be smarter: only rollback IF this was the LATEST pending update
    // This is where dedicated libraries shine.
  }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This manual approach quickly becomes complex. This is why libraries are often preferred.&lt;/p&gt;
&lt;h4 id=&quot;3-server-side-reconciliation-and-idempotency&quot;&gt;3. Server-Side Reconciliation and Idempotency&lt;/h4&gt;
&lt;p&gt;While primarily a client-side concern, the backend plays a crucial role.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Idempotent Endpoints:&lt;/strong&gt; Design your API endpoints such that making the same request multiple times has the same effect as making it once. For example, a &lt;code&gt;PUT /items/{id}&lt;/code&gt; should replace the item, not create duplicates.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Optimistic Concurrency Control:&lt;/strong&gt; Use database versioning or &lt;code&gt;ETag&lt;/code&gt; checks on the server. If a client tries to update a resource with an outdated version, the server rejects the request, forcing the client to re-fetch and retry.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Clear Response Payloads:&lt;/strong&gt; Server responses should clearly indicate the &lt;em&gt;final, authoritative state&lt;/em&gt; of the resource after the operation, not just a simple &quot;success.&quot; This allows the client to reconcile its optimistic state with the true state.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;4-pessimistic-fallback-hybrid-approaches&quot;&gt;4. Pessimistic Fallback / Hybrid Approaches&lt;/h4&gt;
&lt;p&gt;Sometimes, the risk of data inconsistency outweighs the benefit of perceived speed.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Pessimistic Updates:&lt;/strong&gt; For critical operations (e.g., financial transactions, deleting sensitive data, complex multi-step forms), it's often safer to wait for the server's confirmation before updating the UI. Display a loading spinner and disable the UI element.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hybrid Approach:&lt;/strong&gt; Use optimistic updates for low-stakes, frequent interactions (likes, toggles) and pessimistic updates for high-stakes, less frequent actions (deletes, major form submissions).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Trade-offs:&lt;/strong&gt; Sacrifices some perceived performance for guaranteed data integrity.&lt;/p&gt;
&lt;h4 id=&quot;5-advanced-state-management-libraries-react-query-swr-apollo-client-&quot;&gt;5. Advanced State Management Libraries (React Query, SWR, Apollo Client)&lt;/h4&gt;
&lt;p&gt;These libraries are purpose-built to manage asynchronous data fetching and mutations, making optimistic UI implementation significantly safer and simpler. They abstract away much of the complexity of caching, revalidation, and race condition handling.&lt;/p&gt;
&lt;p&gt;Let's revisit our toggle example with &lt;strong&gt;React Query&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import React from 'react';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';

// Simulate an API call with random delays and occasional failures
const fakeApiCall = (newValue) =&amp;gt; new Promise((resolve, reject) =&amp;gt; {
  const delay = Math.random() * 800 + 200; // 200ms to 1s delay
  setTimeout(() =&amp;gt; {
    if (Math.random() &amp;lt; 0.3) { // 30% chance of failure
      reject(new Error(&quot;Network error or server issue&quot;));
    } else {
      resolve({ success: true, newState: newValue });
    }
  }, delay);
});

// Simulate fetching the initial state
const fetchToggleState = async () =&amp;gt; {
  console.log(&quot;Fetching initial toggle state...&quot;);
  await new Promise(res =&amp;gt; setTimeout(res, 500)); // Simulate fetch delay
  return Math.random() &amp;gt; 0.5; // Random initial state
};

function OptimisticToggleReactQuery() {
  const queryClient = useQueryClient();

  // 1. Query to get the current state from the server/cache
  const { data: isOn, isLoading: isQueryLoading, isError: isQueryError } = useQuery({
    queryKey: ['toggleState'],
    queryFn: fetchToggleState,
    staleTime: 5 * 60 * 1000, // Data considered fresh for 5 minutes
    cacheTime: 10 * 60 * 1000, // Data stays in cache for 10 minutes
  });

  // 2. Mutation for updating the state
  const toggleMutation = useMutation({
    mutationFn: async (newValue) =&amp;gt; {
      console.log(`Mutation: Sending API call for: ${newValue}`);
      const response = await fakeApiCall(newValue);
      if (!response.success) {
        throw new Error(&quot;API call failed&quot;);
      }
      return response.newState;
    },
    // This is where the magic happens for optimistic updates
    onMutate: async (newValue) =&amp;gt; {
      // Cancel any outgoing refetches for this query
      // (so they don't overwrite our optimistic update)
      await queryClient.cancelQueries({ queryKey: ['toggleState'] });

      // Snapshot the previous value
      const previousToggleState = queryClient.getQueryData(['toggleState']);
      console.log(`onMutate: Optimistically setting state to ${newValue}. Previous: ${previousToggleState}`);

      // Optimistically update to the new value
      queryClient.setQueryData(['toggleState'], newValue);

      // Return a context object with the snapshotted value
      // This context will be passed to onError and onSettled
      return { previousToggleState };
    },
    // If the mutation fails, use the context for a rollback
    onError: (err, newValue, context) =&amp;gt; {
      console.error(&quot;Optimistic update failed:&quot;, err);
      if (context?.previousToggleState !== undefined) {
        console.log(`onError: Rolling back to ${context.previousToggleState}`);
        queryClient.setQueryData(['toggleState'], context.previousToggleState);
      }
      // You might also want to show a toast notification here
    },
    // Always refetch after error or success to ensure client state matches server
    onSettled: (data, error, variables, context) =&amp;gt; {
      console.log(&quot;onSettled: Invalidating query to refetch latest state from server.&quot;);
      queryClient.invalidateQueries({ queryKey: ['toggleState'] });
    },
  });

  const handleToggle = () =&amp;gt; {
    // toggleMutation.mutate() handles the optimistic update and API call
    toggleMutation.mutate(!isOn);
  };

  if (isQueryLoading) return &amp;lt;p&amp;gt;Loading initial state...&amp;lt;/p&amp;gt;;
  if (isQueryError) return &amp;lt;p style={{ color: 'red' }}&amp;gt;Error loading initial state: {isQueryError.message}&amp;lt;/p&amp;gt;;

  return (
    &amp;lt;div style={{ padding: '20px', border: '1px solid #ccc', borderRadius: '8px' }}&amp;gt;
      &amp;lt;h3&amp;gt;Optimistic Toggle with React Query&amp;lt;/h3&amp;gt;
      &amp;lt;p&amp;gt;Status: &amp;lt;strong style={{ color: isOn ? 'green' : 'red' }}&amp;gt;{isOn ? &quot;On&quot; : &quot;Off&quot;}&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;
      &amp;lt;button onClick={handleToggle} disabled={toggleMutation.isLoading}&amp;gt;
        {toggleMutation.isLoading ? &quot;Updating...&quot; : &quot;Toggle State&quot;}
      &amp;lt;/button&amp;gt;
      {toggleMutation.isError &amp;amp;&amp;amp; &amp;lt;p style={{ color: 'red', marginTop: '10px' }}&amp;gt;Error: {toggleMutation.error.message}&amp;lt;/p&amp;gt;}
      {toggleMutation.isLoading &amp;amp;&amp;amp; &amp;lt;p&amp;gt;Updating...&amp;lt;/p&amp;gt;}
      &amp;lt;p style={{ fontSize: '0.8em', color: '#666', marginTop: '15px' }}&amp;gt;
        React Query handles race conditions by cancelling stale queries, snapping previous state for rollbacks,
        and revalidating data after mutations. Try rapid clicks and observe the console logs.
      &amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;How React Query (and similar libraries) help:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Query Invalidation:&lt;/strong&gt; &lt;code&gt;queryClient.invalidateQueries&lt;/code&gt; tells React Query that the data for a specific key (&lt;code&gt;['toggleState']&lt;/code&gt;) is stale and should be refetched on next access. This ensures eventual consistency.&lt;/li&gt;
&lt;li&gt;**&lt;code&gt;onMutate&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/23205/optimistic-ui-unmasking-the-elusive-race-conditions-hiding-in-plain-sight</guid>
<pubDate>Wed, 22 Jul 2026 16:51:38 +0000</pubDate>
</item>
<item>
<title>Mastering Asynchronous State: Preventing Optimistic UI Race Conditions</title>
<link>https://coderlegion.com/23204/mastering-asynchronous-state-preventing-optimistic-ui-race-conditions</link>
<description>&lt;p&gt;The modern web is built on a promise: speed. Users expect immediate feedback, instantaneous updates, and a seamless flow, even when complex operations are happening behind the scenes. This desire for snappiness has led to the widespread adoption of &quot;Optimistic UIs&quot; – interfaces that assume an operation will succeed and update the UI immediately, only rolling back if the server later reports an error.&lt;/p&gt;
&lt;p&gt;Optimistic UIs are a powerful tool for enhancing user experience, transforming what would otherwise be a jarring wait into a fluid interaction. Think about &quot;liking&quot; a post on social media: the heart icon usually fills instantly, even before your like has been registered on the server. This perceived speed is fantastic.&lt;/p&gt;
&lt;p&gt;However, this optimism comes with a significant caveat. By decoupling the UI state from the true server state, we introduce a temporal discrepancy. This gap, however brief, is a fertile ground for subtle, insidious bugs – the kind that only show up under specific, hard-to-reproduce conditions, like &quot;the fifth click,&quot; or when the network is just flaky enough, or when two users hit the same button at precisely the wrong moment. These are the dreaded race conditions.&lt;/p&gt;
&lt;p&gt;As developers, we often build features that work perfectly in isolation, on our fast local networks, with minimal simulated load. The real world, however, is messy. Users are impatient, networks are unreliable, and servers have varying response times. It's in this chaotic environment that our carefully constructed optimistic Uations can unravel, leading to incorrect data displays, frustrating user experiences, and debugging nightmares.&lt;/p&gt;
&lt;p&gt;This article will delve deep into the world of optimistic UIs and the race conditions they can breed. We'll explore why these bugs are so challenging to identify, dissect their anatomy with practical examples, and, most importantly, equip you with a comprehensive toolkit of strategies and best practices to build resilient, robust, and truly reliable optimistic user interfaces.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-allure-of-optimistic-uis-speed-and-smoothness&quot;&gt;The Allure of Optimistic UIs: Speed and Smoothness&lt;/h3&gt;
&lt;p&gt;Before we tackle the problems, let's appreciate the benefits. An Optimistic UI operates on the principle of &quot;presumed success.&quot; When a user initiates an action, instead of waiting for a server response to confirm success, the UI immediately reflects the &lt;em&gt;expected&lt;/em&gt; outcome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Consider a simple &quot;toggle complete&quot; checkbox for a to-do item:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Without an optimistic UI:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;User clicks checkbox.&lt;/li&gt;
&lt;li&gt;Checkbox remains unchecked (or shows a loading spinner).&lt;/li&gt;
&lt;li&gt;API call is sent to the server.&lt;/li&gt;
&lt;li&gt;Server processes request and responds.&lt;/li&gt;
&lt;li&gt;On success, checkbox updates to checked.&lt;/li&gt;
&lt;li&gt;On failure, checkbox reverts and an error message appears.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This approach is safe but introduces noticeable latency. For every interaction, the user experiences a delay, however small.&lt;/p&gt;
&lt;p&gt;With an Optimistic UI:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;User clicks checkbox.&lt;/li&gt;
&lt;li&gt;Checkbox &lt;em&gt;immediately&lt;/em&gt; updates to checked.&lt;/li&gt;
&lt;li&gt;API call is sent to the server in the background.&lt;/li&gt;
&lt;li&gt;If the server responds with success, nothing visibly changes (the UI is already in the correct state).&lt;/li&gt;
&lt;li&gt;If the server responds with an error, the checkbox reverts to unchecked, and an error message appears.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The difference in user perception is profound. The UI feels instant, responsive, and fluid. This pattern is particularly effective for actions that are generally low-risk and frequently performed, like liking, following, toggling, or adding items to a list where the order doesn't strictly matter immediately.&lt;/p&gt;
&lt;h3 id=&quot;the-dark-side-of-optimism-introducing-race-conditions&quot;&gt;The Dark Side of Optimism: Introducing Race Conditions&lt;/h3&gt;
&lt;p&gt;The very mechanism that makes optimistic UIs so appealing – the temporary divergence between client and server state – is also its Achilles' heel. When multiple asynchronous operations occur rapidly or in an unexpected order, a &quot;race condition&quot; can emerge.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What is a Race Condition?&lt;/strong&gt;&lt;br&gt;
A race condition occurs when the correctness of a program depends on the relative timing or interleaving of multiple threads, processes, or, in our case, asynchronous operations. If these operations execute in an unexpected order, or if one operation's outcome is overwritten by another's, the program can enter an erroneous state.&lt;/p&gt;
&lt;p&gt;In the context of optimistic UIs, race conditions typically manifest when:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Multiple, rapid identical actions:&lt;/strong&gt; A user clicks a button multiple times quickly (e.g., &quot;like&quot; or &quot;increment quantity&quot;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Overlapping distinct actions:&lt;/strong&gt; A user performs action A, then immediately action B, and the server responses arrive out of order.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Network variability:&lt;/strong&gt; Latency spikes, packet loss, or server processing delays cause API responses to arrive much later or out of sequence compared to the requests.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;the-fifth-click-phenomenon-why-intermittent-bugs-are-the-worst&quot;&gt;The &quot;Fifth Click&quot; Phenomenon: Why Intermittent Bugs are the Worst&lt;/h4&gt;
&lt;p&gt;The source article highlights a bug that &quot;only showed up on the fifth click.&quot; This is a classic characteristic of race conditions and other timing-dependent issues. Why don't they show up consistently?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Timing Windows:&lt;/strong&gt; Race conditions depend on a very specific, often narrow, window of time where operations overlap in a problematic way. Most of the time, operations complete sequentially enough that the bug doesn't trigger.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Network Jitter:&lt;/strong&gt; Your local development environment often has negligible network latency. In production, network conditions fluctuate wildly. A slight delay in one API response, combined with a quick subsequent user action, can create that perfect storm.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Server Load:&lt;/strong&gt; Under heavy load, server response times can increase, again widening the timing window for race conditions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Client-Side Event Loop:&lt;/strong&gt; The browser's event loop processes tasks. Rapid user input can queue up multiple events, and if API responses arrive between these event dispatches, unexpected state transitions can occur.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Caching and Optimizations:&lt;/strong&gt; Browsers, proxies, and even network hardware can introduce their own layers of caching and reordering, further complicating the sequence of events.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &quot;fifth click&quot; isn't magical; it's just the &lt;em&gt;one&lt;/em&gt; specific sequence of events (clicks, network requests, server responses, client-side state updates) that happened to align in a way that exposed the underlying flaw. Reproducing such bugs is notoriously difficult because you need to recreate that exact, ephemeral timing window.&lt;/p&gt;
&lt;h4 id=&quot;anatomy-of-a-race-condition-in-an-optimistic-ui&quot;&gt;Anatomy of a Race Condition in an Optimistic UI&lt;/h4&gt;
&lt;p&gt;Let's illustrate with a common scenario: a &quot;like&quot; button.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Desired Behavior:&lt;/strong&gt;&lt;br&gt;
User clicks &quot;like&quot; -&amp;gt; UI shows +1 like -&amp;gt; Server confirms -&amp;gt; State is consistent.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Scenario for a Race Condition:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Initial State:&lt;/strong&gt; Post has 10 likes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;User Action 1 (Click 1):&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;Client UI immediately updates: &lt;code&gt;likes = 11&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;API request &lt;code&gt;POST /posts/123/like&lt;/code&gt; is sent (Request A).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;User Action 2 (Click 2, very quickly):&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;Client UI immediately updates: &lt;code&gt;likes = 12&lt;/code&gt;. (Based on current UI state, not server state)&lt;/li&gt;
&lt;li&gt;API request &lt;code&gt;POST /posts/123/like&lt;/code&gt; is sent (Request B).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Network/Server Latency:&lt;/strong&gt; Request A is slightly delayed on the server. Request B gets processed faster.&lt;ul&gt;
&lt;li&gt;Server processes Request B first: &lt;code&gt;likes&lt;/code&gt; becomes 11. Responds with success.&lt;/li&gt;
&lt;li&gt;Server processes Request A second: &lt;code&gt;likes&lt;/code&gt; becomes 12. Responds with success.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Client Receives Responses:&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;Client receives success for Request B. What does it do? If it just updates &lt;code&gt;likes&lt;/code&gt; to 11 (from server), it overwrites the UI's current 12.&lt;/li&gt;
&lt;li&gt;Client receives success for Request A. If it just updates &lt;code&gt;likes&lt;/code&gt; to 12, it looks correct &lt;em&gt;now&lt;/em&gt;, but what if Request B's response arrives &lt;em&gt;after&lt;/em&gt; A's?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is a simplified example. The true complexity arises when the client-side state management library tries to reconcile these updates, especially if it's not designed to handle out-of-order responses or if the server response contains the &lt;em&gt;new total&lt;/em&gt; rather than just a confirmation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Illustrative Code Snippet (Conceptual React Component):&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import React, { useState } from 'react';
import axios from 'axios';

function LikeButton({ postId, initialLikes }) {
  const [likes, setLikes] = useState(initialLikes);
  const [isLiking, setIsLiking] = useState(false); // Basic debounce

  const handleLike = async () =&amp;gt; {
    if (isLiking) return; // Prevent multiple requests from the same button press

    const previousLikes = likes;
    setLikes(likes + 1); // Optimistic update
    setIsLiking(true);

    try {
      // Simulate network delay and potential out-of-order response
      const response = await axios.post(`/api/posts/${postId}/like`, { action: 'increment' });
      // If server sends back the new total, this might overwrite a subsequent optimistic update
      // setLikes(response.data.newLikesCount); // This is where the race condition can manifest

      // If we just assume success and don't update with server data,
      // subsequent clicks are based on client's optimistic count.
    } catch (error) {
      setLikes(previousLikes); // Rollback on error
      console.error(&quot;Failed to like post:&quot;, error);
      // Show user error message
    } finally {
      setIsLiking(false);
    }
  };

  return (
    &amp;lt;button onClick={handleLike} disabled={isLiking}&amp;gt;
      ❤️ {likes} Likes
    &amp;lt;/button&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this basic example, &lt;code&gt;isLiking&lt;/code&gt; prevents &lt;em&gt;identical&lt;/em&gt; rapid clicks from the &lt;em&gt;same&lt;/em&gt; button instance. But what if a user opens two tabs and clicks simultaneously? Or if &lt;code&gt;setLikes(response.data.newLikesCount)&lt;/code&gt; is used, and a later response (from an earlier request) overwrites a more recent, correct count? The problem space expands quickly.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;strategies-for-taming-optimistic-ui-race-conditions&quot;&gt;Strategies for Taming Optimistic UI Race Conditions&lt;/h3&gt;
&lt;p&gt;Building robust optimistic UIs requires a multi-pronged approach, combining client-side defensive programming with smart server-side design.&lt;/p&gt;
&lt;h4 id=&quot;1-debouncing-and-throttling-user-input&quot;&gt;1. Debouncing and Throttling User Input&lt;/h4&gt;
&lt;p&gt;The simplest first line of defense is to prevent users from sending an excessive number of identical requests in a short period.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Debouncing:&lt;/strong&gt; Ensures a function is only called after a certain period of inactivity. If the user clicks rapidly, the function is reset each time, only firing once the clicks stop.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Throttling:&lt;/strong&gt; Ensures a function is called at most once within a specified time frame. If the user clicks rapidly, it will fire once, then ignore subsequent clicks until the time window passes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Example (Debounce):&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// Utility function
const debounce = (func, delay) =&amp;gt; {
  let timeout;
  return function(...args) {
    const context = this;
    clearTimeout(timeout);
    timeout = setTimeout(() =&amp;gt; func.apply(context, args), delay);
  };
};

// In your component
const handleLikeDebounced = debounce(async () =&amp;gt; {
  // ... optimistic update logic and API call ...
}, 300); // Wait 300ms after last click
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Trade-offs:&lt;/strong&gt; Can slightly delay feedback for legitimate rapid actions, but prevents many accidental duplicate requests.&lt;/p&gt;
&lt;h4 id=&quot;2-request-queuing-and-sequencing-client-side-&quot;&gt;2. Request Queuing and Sequencing (Client-Side)&lt;/h4&gt;
&lt;p&gt;For more complex scenarios, especially when multiple distinct but related actions can occur, managing the order of responses becomes critical.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Assign Unique IDs:&lt;/strong&gt; Each outgoing request should have a unique client-generated ID.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Maintain a Pending Queue:&lt;/strong&gt; Store optimistic updates and their corresponding request IDs in a client-side queue.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Process Responses in Order:&lt;/strong&gt; When a response arrives, use its ID to find the original request. If there are earlier requests still pending, hold off on applying the response's data until all preceding responses have been processed. This is complex to implement manually.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Conceptual Example:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// Imagine a service or hook
const useRequestQueue = () =&amp;gt; {
  const [pendingRequests, setPendingRequests] = useState([]);
  const [state, setState] = useState({}); // Your actual data state

  const sendRequest = async (action, payload) =&amp;gt; {
    const requestId = Date.now().toString() + Math.random().toString(36).substring(2);
    // Optimistically update state based on action
    const optimisticUpdate = calculateOptimisticState(state, action, payload);
    setState(optimisticUpdate);

    setPendingRequests(prev =&amp;gt; [...prev, { requestId, action, payload, optimisticUpdate }]);

    try {
      const response = await apiCall(action, payload, requestId); // Send requestId to server
      // On success, remove from pending. If the response carries server's definitive state,
      // we need a mechanism to reconcile it with any *subsequent* optimistic updates.
      // This is often where libraries shine.
      setPendingRequests(prev =&amp;gt; prev.filter(req =&amp;gt; req.requestId !== requestId));
      // Reconcile state based on response and remaining pending requests.
    } catch (error) {
      // On error, revert the specific optimistic update associated with requestId
      setPendingRequests(prev =&amp;gt; prev.filter(req =&amp;gt; req.requestId !== requestId));
      setState(revertOptimisticState(state, action, payload, optimisticUpdate));
    }
  };

  return { state, sendRequest };
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This manual approach is prone to bugs. This is precisely why state management libraries like React Query, SWR, and Apollo Client exist.&lt;/p&gt;
&lt;h4 id=&quot;3-leverage-dedicated-state-management-libraries-highly-recommended-&quot;&gt;3. Leverage Dedicated State Management Libraries (Highly Recommended)&lt;/h4&gt;
&lt;p&gt;Modern data fetching and state management libraries are &lt;em&gt;designed&lt;/em&gt; to handle the complexities of optimistic UIs, caching, and eventual consistency. They abstract away much of the boilerplate and risk.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;React Query (TanStack Query) Example:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;import { useQueryClient, useMutation } from '@tanstack/react-query';
import axios from 'axios';

function LikeButtonWithReactQuery({ postId, initialLikes }) {
  const queryClient = useQueryClient();

  // Initial data fetch for the post
  const { data: post } = queryClient.getQueryData(['post', postId]) || { likes: initialLikes };
  const currentLikes = post.likes;

  const mutation = useMutation({
    mutationFn: (newLikesCount) =&amp;gt; axios.post(`/api/posts/${postId}/like`, { likes: newLikesCount }),
    // `onMutate` is called before the mutation function is fired and can be used to
    // perform an optimistic update.
    onMutate: async (newLikesCount) =&amp;gt; {
      // Cancel any outgoing refetches for this query to avoid overwriting our optimistic update
      await queryClient.cancelQueries(['post', postId]);

      // Snapshot the previous value
      const previousPost = queryClient.getQueryData(['post', postId]);

      // Optimistically update to the new value
      queryClient.setQueryData(['post', postId], old =&amp;gt; ({ ...old, likes: newLikesCount }));

      // Return a context object with the snapshot value
      return { previousPost };
    },
    // If the mutation fails, use the context object from onMutate to roll back
    onError: (err, newLikesCount, context) =&amp;gt; {
      queryClient.setQueryData(['post', postId], context.previousPost);
      console.error(&quot;Failed to like post:&quot;, err);
      // Show user error message
    },
    // Always refetch after error or success:
    onSettled: () =&amp;gt; {
      queryClient.invalidateQueries(['post', postId]);
    },
  });

  const handleLike = () =&amp;gt; {
    // Determine the next state based on current UI state (optimistic)
    const nextLikes = currentLikes + 1;
    mutation.mutate(nextLikes);
  };

  return (
    &amp;lt;button onClick={handleLike} disabled={mutation.isLoading}&amp;gt;
      ❤️ {currentLikes} Likes
    &amp;lt;/button&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Key benefits of libraries like React Query:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Automatic Cache Management:&lt;/strong&gt; Handles caching, invalidation, and background re-fetching.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Optimistic Updates with Rollback:&lt;/strong&gt; &lt;code&gt;onMutate&lt;/code&gt; and &lt;code&gt;onError&lt;/code&gt; hooks provide a structured way to perform optimistic updates and revert them if the mutation fails.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stale-While-Revalidate (SWR):&lt;/strong&gt; Provides immediate data while revalidating in the background, crucial for optimistic UIs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deduplication and Sequencing:&lt;/strong&gt; Often handles multiple concurrent requests to the same endpoint, preventing race conditions by ensuring only one request is active or by processing responses in a sensible order.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;4-server-side-idempotency-and-versioning&quot;&gt;4. Server-Side Idempotency and Versioning&lt;/h4&gt;
&lt;p&gt;Client-side solutions are vital, but a robust backend is equally important.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Idempotent Endpoints:&lt;/strong&gt; Design your API endpoints such that making the same request multiple times has the same effect as making it once.&lt;ul&gt;
&lt;li&gt;Instead of &lt;code&gt;POST /increment-likes&lt;/code&gt;, which would increment every time, consider &lt;code&gt;PUT /set-likes/{id}&lt;/code&gt; with a specific value, or &lt;code&gt;PATCH /like/{id}&lt;/code&gt; which toggles or ensures a single like per user.&lt;/li&gt;
&lt;li&gt;For &quot;liking,&quot; the server should ideally store a record of which &lt;em&gt;user&lt;/em&gt; liked which &lt;em&gt;post&lt;/em&gt;, not just a count. When a user clicks &quot;like,&quot; the server should check if that user has already liked the post. If so, it does nothing or updates an existing record.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Optimistic Concurrency Control (ETags/Version Fields):&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;When fetching an entity, the server can include a version identifier (e.g., an ETag HTTP header, or a &lt;code&gt;version&lt;/code&gt; field in the JSON payload).&lt;/li&gt;
&lt;li&gt;When the client sends an update, it includes this version identifier.&lt;/li&gt;
&lt;li&gt;The server then checks if the version it received matches the current version of the resource. If not, it means the resource was modified by someone else (or another client-side action) since the client last fetched it. The server can then reject the update (e.g., HTTP 409 Conflict) and instruct the client to re-fetch the latest data.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Example (Server-side pseudo-code for optimistic concurrency):&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;# Django/Flask-like pseudo-code
@app.route('/api/posts/&amp;lt;int:post_id&amp;gt;', methods=['PUT'])
def update_post(post_id):
    data = request.json
    client_version = data.get('version') # Client sends the version it knows

    with db.session() as session:
        post = session.query(Post).filter_by(id=post_id).first()
        if not post:
            return jsonify({'message': 'Post not found'}), 404

        if post.version != client_version:
            # Conflict! Resource was modified by another request.
            return jsonify({
                'message': 'Conflict: resource has been updated by another user/process. Please re-fetch and try again.',
                'currentVersion': post.version,
                'currentLikes': post.likes # Provide latest data to help client reconcile
            }), 409

        # Apply updates
        post.likes = data.get('likes', post.likes)
        post.version += 1 # Increment version on successful update
        session.commit()
        return jsonify({'message': 'Post updated', 'version': post.version, 'likes': post.likes}), 200
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This mechanism forces the client to deal with stale data explicitly, preventing it from blindly overwriting valid server state.&lt;/p&gt;
&lt;h4 id=&quot;5-robust-error-handling-and-rollbacks&quot;&gt;5. Robust Error Handling and Rollbacks&lt;/h4&gt;
&lt;p&gt;The &quot;optimistic&quot; part implies that success is the norm. But we must be prepared for failure.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Explicit Rollback Logic:&lt;/strong&gt; Every optimistic update must have a clear rollback path if the server request fails. This means storing the previous state before the optimistic update.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;User Feedback:&lt;/strong&gt; When a rollback occurs, it's crucial to inform the user. A subtle toast message (&quot;Failed to like post, please try again&quot;) is better than silent failure.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Retry Mechanisms:&lt;/strong&gt; For transient network errors, consider offering a &quot;retry&quot; option.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;6-defensive-ui-design&quot;&gt;6. Defensive UI Design&lt;/h4&gt;
&lt;p&gt;Sometimes, the simplest solutions are the most effective.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Disable Buttons:&lt;/strong&gt; While an API request is in flight for a specific action, disable the corresponding button or input field. This prevents users from triggering multiple identical requests.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Loading Indicators:&lt;/strong&gt; Visually indicate that an operation is pending. This manages user expectations and reduces the urge for rapid re-clicks.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Clear State Communication:&lt;/strong&gt; For actions like toggling, show an intermediate &quot;pending&quot; state rather than immediately flipping to the final state if the action is critical or prone to contention.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;7-observability-and-monitoring&quot;&gt;7. Observability and Monitoring&lt;/h4&gt;
&lt;p&gt;You can't fix what you can't see.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Client-Side Logging:&lt;/strong&gt; Log optimistic updates, API request/response timings, and any rollbacks. Tools like Sentry (as referenced in the source article) are excellent for capturing these client-side errors and performance metrics in production.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Server-Side Logging:&lt;/strong&gt; Correlate client request IDs with server logs to trace the full lifecycle of an operation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Performance Monitoring:&lt;/strong&gt; Keep an eye on API latency. High latency widens the window for race conditions.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-trade-offs-when-to-be-optimistic-when-to-be-cautious&quot;&gt;The Trade-Offs: When to Be Optimistic, When to Be Cautious&lt;/h3&gt;
&lt;p&gt;Optimistic UIs are not a silver bullet. Deciding when and where to deploy them involves weighing user experience against implementation complexity and the risk of data inconsistency.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Use Optimistic UIs When:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Low Impact Actions:&lt;/strong&gt; Liking, following, starring, simple toggles where a temporary inconsistency is acceptable.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;High Frequency Interactions:&lt;/strong&gt; Actions users perform repeatedly, where any delay would be frustrating.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Non-Critical Data:&lt;/strong&gt; Data that isn't financial, legal, or mission-critical.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Idempotent Operations:&lt;/strong&gt; Server-side operations that can be safely repeated without adverse effects.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Be Cautious or Avoid Optimistic UIs When:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;High Impact Actions:&lt;/strong&gt; Financial transactions, deleting critical data, submitting forms with complex side effects.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Complex Interdependencies:&lt;/strong&gt; Actions that heavily affect other parts of the UI or other users (e.g., updating a shared document where real-time accuracy is paramount).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;High Contention:&lt;/strong&gt; Scenarios where multiple users are likely to attempt to modify the same resource simultaneously.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Non-Idempotent Operations:&lt;/strong&gt; Operations that would cause incorrect state if repeated (e.g., &lt;code&gt;POST /create-new-record&lt;/code&gt; without uniqueness constraints).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Unreliable Networks/Servers:&lt;/strong&gt; If your target audience has extremely poor network conditions or your backend is frequently unstable, the rollback experience might become more frustrating than simply waiting.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For critical operations, a traditional &quot;loading spinner&quot; or a &quot;confirmation dialog&quot; followed by server-verified updates is often the safer, more robust choice, even if it introduces a slight delay.&lt;/p&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Optimistic UIs are a cornerstone of modern, highly responsive web applications. They elevate the user experience by providing immediate feedback, making applications feel faster and more delightful. However, this perceived speed comes at a cost: increased complexity in managing client-side state and a heightened risk of race conditions.&lt;/p&gt;
&lt;p&gt;The &quot;fifth click&quot; bug, or any intermittent, timing-dependent issue, serves as a stark reminder that asynchronous operations and distributed systems are inherently complex. Understanding the potential pitfalls – from rapid user input to network latency and server processing variations – is the first step towards building resilient systems.&lt;/p&gt;
&lt;p&gt;By adopting a comprehensive strategy that includes client-side debouncing, leveraging powerful state management libraries like React Query, designing idempotent and versioned server APIs, implementing robust error handling with clear rollbacks, and applying defensive UI design principles, developers can mitigate the risks associated with optimistic UIs.&lt;/p&gt;
&lt;p&gt;Ultimately, the goal is to strike a balance: harness the power of optimism to create exceptional user experiences, but temper it with a healthy dose of defensive programming and a deep understanding of asynchronous state management. The result will be applications that not only feel fast but are also reliable and a pleasure to debug (or rather, &lt;em&gt;not&lt;/em&gt; to debug).&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/23204/mastering-asynchronous-state-preventing-optimistic-ui-race-conditions</guid>
<pubDate>Wed, 22 Jul 2026 12:14:34 +0000</pubDate>
</item>
<item>
<title>Optimistic UIs: Navigating the Race for Seamless User Experience</title>
<link>https://coderlegion.com/23206/optimistic-uis-navigating-the-race-for-seamless-user-experience</link>
<description>&lt;p&gt;User interfaces have come a long way from static pages, evolving into dynamic, interactive experiences that strive for instant gratification. In this pursuit of perceived speed and responsiveness, the &quot;optimistic UI&quot; pattern has emerged as a powerful tool. It allows applications to react immediately to user actions, assuming success, and only reverting the UI if a backend operation ultimately fails. The user clicks &quot;Like,&quot; and the heart icon instantly fills, conveying an immediate sense of accomplishment, even before the server has confirmed the action.&lt;/p&gt;
&lt;p&gt;This approach is undeniably appealing, offering a significant boost to user experience by eliminating frustrating loading spinners and perceived latency. However, beneath this veneer of instantaneous feedback lies a complex dance between client-side state and server-side truth. This dance, if choreographed imperfectly, can lead to subtle, insidious bugs – the kind that only manifest under specific, often hard-to-reproduce conditions. Imagine a race condition that doesn't just appear on the first or second interaction, but only after a specific sequence of actions, perhaps on the fifth click, or the tenth. These &quot;fifth click&quot; bugs are the specters haunting optimistic UIs, challenging even the most seasoned developers to unravel their mysteries.&lt;/p&gt;
&lt;p&gt;This article delves deep into the world of optimistic UIs, exploring their undeniable benefits, dissecting the precise nature of the race conditions they can engender, and, most importantly, providing robust strategies and practical advice to build resilient, reliable systems that offer both speed and accuracy.&lt;/p&gt;
&lt;h3 id=&quot;the-promise-and-peril-of-optimistic-uis&quot;&gt;The Promise and Peril of Optimistic UIs&lt;/h3&gt;
&lt;p&gt;Optimistic UIs are a cornerstone of modern web application development, designed to enhance the perceived performance and responsiveness of applications. But what exactly are they, and what are the inherent challenges they introduce?&lt;/p&gt;
&lt;h4 id=&quot;what-is-optimistic-ui-&quot;&gt;What is Optimistic UI?&lt;/h4&gt;
&lt;p&gt;At its core, an optimistic UI is a user interface that updates immediately in response to a user action, &lt;em&gt;before&lt;/em&gt; receiving confirmation from the server that the action was successful. The UI &quot;optimistically&quot; assumes the action will succeed. If the server eventually confirms success, no further UI update is needed. If the server reports an error, the UI then &quot;rolls back&quot; to its previous state, informing the user of the failure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Consider a simple example: Toggling a &quot;Like&quot; button.&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;User Clicks &quot;Like&quot;:&lt;/strong&gt; The client-side application immediately changes the button's state from &quot;unliked&quot; to &quot;liked&quot; (e.g., fills a heart icon, increments a counter).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;API Request Sent:&lt;/strong&gt; Simultaneously, an asynchronous request is sent to the backend API to record the &quot;like.&quot;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Server Response:&lt;/strong&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Success:&lt;/strong&gt; The server processes the request and sends a success response. The UI, already updated, remains as is.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Failure:&lt;/strong&gt; The server encounters an error (e.g., network issue, authentication failure, database error). The client receives an error response, and the UI reverts the &quot;Like&quot; button to its &quot;unliked&quot; state, perhaps displaying an error message.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Benefits:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Perceived Performance:&lt;/strong&gt; Users experience immediate feedback, making the application feel faster and more responsive, even if network latency is present.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Improved User Experience:&lt;/strong&gt; Reduced waiting times lead to smoother interactions and higher user satisfaction.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reduced Cognitive Load:&lt;/strong&gt; Users don't have to track pending operations; the UI just &quot;works.&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;the-underlying-challenge-asynchronicity-and-eventual-consistency&quot;&gt;The Underlying Challenge: Asynchronicity and Eventual Consistency&lt;/h4&gt;
&lt;p&gt;The fundamental challenge with optimistic UIs stems from the inherent asynchronous nature of client-server communication and the principle of eventual consistency. The client-side state (what the user sees) and the server-side state (the single source of truth) are temporarily decoupled.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Asynchronicity:&lt;/strong&gt; Network requests are non-blocking. The client sends a request and continues executing code. The server processes the request and responds at an unpredictable time. This time gap is where race conditions thrive.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Eventual Consistency:&lt;/strong&gt; This principle states that given enough time, the client's state will eventually become consistent with the server's state. The &quot;eventual&quot; part is key. During the window between the optimistic update and the server's definitive response, the client's UI might be showing a state that isn't yet true, or worse, is completely out of sync if subsequent actions or failures aren't handled meticulously.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This temporary divergence is precisely what makes optimistic UIs powerful, but also dangerous. Without careful management, a sequence of optimistic updates, combined with network delays, server errors, or out-of-order responses, can lead to a client-side state that is fundamentally incorrect and resistant to simple corrections. This is the breeding ground for the elusive &quot;fifth click&quot; race condition.&lt;/p&gt;
&lt;h3 id=&quot;unmasking-the-elusive-race-condition&quot;&gt;Unmasking the Elusive Race Condition&lt;/h3&gt;
&lt;p&gt;A race condition occurs when two or more operations attempt to access and modify the same shared resource concurrently, and the final outcome depends on the non-deterministic order in which these operations complete. In the context of optimistic UIs, the &quot;shared resource&quot; is often the client-side application state.&lt;/p&gt;
&lt;h4 id=&quot;how-race-conditions-manifest-in-optimistic-uis&quot;&gt;How Race Conditions Manifest in Optimistic UIs&lt;/h4&gt;
&lt;p&gt;Unlike simple race conditions where two concurrent requests might overwrite each other, the &quot;fifth click&quot; scenario is far more subtle. It typically involves a sequence of operations where:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;An initial optimistic update occurs.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A network request is sent.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;This request is delayed or fails.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Before the delayed/failed request resolves, one or more &lt;em&gt;subsequent&lt;/em&gt; optimistic updates occur and &lt;em&gt;succeed&lt;/em&gt; on the server.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The delayed/failed request finally resolves (or times out), triggering a rollback or error handling that inadvertently corrupts the state established by the &lt;em&gt;successful subsequent&lt;/em&gt; operations.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The &quot;fifth click&quot; designation highlights that these bugs often don't appear immediately. A single failure is usually handled gracefully. It's the specific &lt;em&gt;interleaving&lt;/em&gt; of successes and failures, especially when coupled with multiple, chained optimistic updates, that creates a desynchronized state that's hard to debug.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example: A &quot;Toggle Item Status&quot; Feature&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Imagine a list of items, each with a toggle button to mark it as &lt;code&gt;active&lt;/code&gt; or &lt;code&gt;inactive&lt;/code&gt;. The UI optimistically updates the item's status immediately.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scenario 1 (Simple Failure):&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;User clicks &quot;Activate&quot; for Item A. UI shows Item A as &lt;code&gt;active&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;API request for Item A fails.&lt;/li&gt;
&lt;li&gt;UI reverts Item A to &lt;code&gt;inactive&lt;/code&gt;. (Graceful)&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scenario 2 (Elusive Race Condition - &quot;Fifth Click&quot;):&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;User clicks &quot;Activate&quot; for Item A. UI shows Item A as &lt;code&gt;active&lt;/code&gt;. (Request R1 sent)&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Network delay&lt;/em&gt; for R1.&lt;/li&gt;
&lt;li&gt;User clicks &quot;Deactivate&quot; for Item A. UI shows Item A as &lt;code&gt;inactive&lt;/code&gt;. (Request R2 sent)&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Network delay&lt;/em&gt; for R2.&lt;/li&gt;
&lt;li&gt;User clicks &quot;Activate&quot; for Item A. UI shows Item A as &lt;code&gt;active&lt;/code&gt;. (Request R3 sent)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Server processes R3 (Activate) and responds success.&lt;/strong&gt; UI state for Item A remains &lt;code&gt;active&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Server processes R2 (Deactivate) and responds success.&lt;/strong&gt; The client-side state management, if not careful, might process this &lt;em&gt;after&lt;/em&gt; R3, inadvertently setting Item A to &lt;code&gt;inactive&lt;/code&gt; again, even though the user's &lt;em&gt;latest&lt;/em&gt; successful action was to activate it. The UI is now out of sync.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Server processes R1 (Activate) and responds &lt;em&gt;failure&lt;/em&gt;.&lt;/strong&gt; The client's error handling for R1 might try to revert the state, potentially further corrupting or confusing the UI, despite R2 and R3 having already completed successfully.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The key here is that the server responses arrive out of the logical sequence of user actions, and the client-side state updates don't properly account for the &lt;em&gt;latest intended state&lt;/em&gt; or the &lt;em&gt;order of server confirmations&lt;/em&gt;.&lt;/p&gt;
&lt;h4 id=&quot;the-role-of-multiple-operations-and-state-dependencies&quot;&gt;The Role of Multiple Operations and State Dependencies&lt;/h4&gt;
&lt;p&gt;Why do these bugs often require multiple actions?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Cumulative Effect:&lt;/strong&gt; A single optimistic update might be easy to roll back. However, when multiple optimistic updates are layered on top of each other, especially if some succeed and some fail, the client-side state can drift significantly from the server's true state.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dependent Actions:&lt;/strong&gt; Many UI actions are dependent on the &lt;em&gt;current&lt;/em&gt; state. If the client-side state is optimistically &lt;code&gt;active&lt;/code&gt;, and the user clicks &quot;Deactivate,&quot; the request sent might be &lt;code&gt;PATCH /item/A { status: 'inactive' }&lt;/code&gt;. If the client-side &lt;code&gt;active&lt;/code&gt; state was actually incorrect due to a previous race condition, the &quot;Deactivate&quot; action might be applied to an already incorrect base, exacerbating the problem.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stale Data:&lt;/strong&gt; If the optimistic update logic doesn't properly revalidate or re-fetch data after a complex sequence of operations, the UI can continue to display stale or incorrect information, even after some requests have succeeded.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This complexity underscores the need for robust state management strategies that can correctly reconcile client-side optimism with server-side reality, even when the network is chaotic.&lt;/p&gt;
&lt;h3 id=&quot;deep-dive-into-state-management-strategies-for-robust-optimistic-uis&quot;&gt;Deep Dive into State Management Strategies for Robust Optimistic UIs&lt;/h3&gt;
&lt;p&gt;Building resilient optimistic UIs requires more than just toggling a boolean. It demands sophisticated state management that can handle the unpredictable nature of network requests.&lt;/p&gt;
&lt;h4 id=&quot;local-state-vs-global-state-management&quot;&gt;Local State vs. Global State Management&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Local Component State (e.g., React &lt;code&gt;useState&lt;/code&gt;, Vue &lt;code&gt;data&lt;/code&gt;):&lt;/strong&gt; Suitable for simple, isolated optimistic updates (e.g., a single toggle within a component). Managing race conditions here can be tricky as state is localized and difficult to coordinate across multiple operations or components.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Global State Management (e.g., Redux, Zustand, Recoil, Vuex):&lt;/strong&gt; Preferable for complex applications with interdependent optimistic updates. A centralized store allows for a single source of truth, easier tracking of pending requests, and more robust rollback mechanisms. Libraries like &lt;code&gt;redux-saga&lt;/code&gt; or &lt;code&gt;redux-thunk&lt;/code&gt; can help manage the asynchronous flow.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Regardless of the choice, the principles of handling concurrency remain similar.&lt;/p&gt;
&lt;h4 id=&quot;request-identification-and-ordering&quot;&gt;Request Identification and Ordering&lt;/h4&gt;
&lt;p&gt;One of the most critical aspects is to uniquely identify each optimistic update and its corresponding network request. This allows you to match server responses to the specific client-side action they pertain to, irrespective of the order in which responses arrive.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// A simple conceptual example using a map to track pending updates
const pendingUpdates = new Map(); // Map&amp;lt;requestId, { originalState, optimisticState, timeoutId }&amp;gt;
let nextRequestId = 0;

async function performOptimisticUpdate(item, newStatus) {
  const requestId = nextRequestId++;
  const originalStatus = item.status;

  // 1. Optimistically update UI
  item.status = newStatus; // In a real app, this would trigger a UI re-render

  // Store original state for potential rollback
  pendingUpdates.set(requestId, {
    item,
    originalStatus,
    optimisticStatus: newStatus,
    // Set a timeout to consider the request failed if no response within X seconds
    timeoutId: setTimeout(() =&amp;gt; handleRequestTimeout(requestId), 10000),
  });

  try {
    const response = await fetch(`/api/items/${item.id}/status`, {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ status: newStatus }),
    });

    clearTimeout(pendingUpdates.get(requestId).timeoutId);

    if (!response.ok) {
      throw new Error('Server error');
    }

    // 2. Server confirmed success - remove from pending, state is correct
    pendingUpdates.delete(requestId);
    // Optionally, re-fetch or normalize data to ensure full consistency
    console.log(`Request ${requestId} for item ${item.id} succeeded.`);

  } catch (error) {
    // 3. Server reported error or network failed - rollback UI
    clearTimeout(pendingUpdates.get(requestId).timeoutId);
    const pending = pendingUpdates.get(requestId);
    if (pending) {
      pending.item.status = pending.originalStatus; // Revert UI
      console.error(`Request ${requestId} for item ${item.id} failed. Rolling back.`);
      pendingUpdates.delete(requestId);
    }
    // Display user-friendly error message
  }
}

function handleRequestTimeout(requestId) {
  const pending = pendingUpdates.get(requestId);
  if (pending) {
    pending.item.status = pending.originalStatus; // Revert UI
    console.warn(`Request ${requestId} for item ${pending.item.id} timed out. Rolling back.`);
    pendingUpdates.delete(requestId);
  }
}

// Example usage:
const myItem = { id: 'abc-123', status: 'inactive' };
performOptimisticUpdate(myItem, 'active');
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This rudimentary example shows how &lt;code&gt;requestId&lt;/code&gt; helps track a specific operation. More advanced solutions often involve a queue or a stack of pending mutations, ensuring that rollbacks or updates are applied in a way that respects the user's latest actions. Libraries like &lt;code&gt;react-query&lt;/code&gt; or &lt;code&gt;SWR&lt;/code&gt; abstract much of this complexity, providing robust mutation management with built-in optimistic update capabilities and automatic revalidation.&lt;/p&gt;
&lt;h4 id=&quot;rollback-and-revalidation-mechanisms&quot;&gt;Rollback and Revalidation Mechanisms&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Rollback on Error:&lt;/strong&gt; When a server request fails, the UI must revert to its pre-optimistic state. This seems straightforward, but becomes complex if multiple optimistic updates are pending. Should a failure of one request roll back &lt;em&gt;all&lt;/em&gt; subsequent optimistic updates? Generally, no. Only the specific state related to the failed request should revert. This requires careful tracking of the &quot;state before change&quot; for each optimistic operation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Revalidation on Success:&lt;/strong&gt; Even after a successful server response, it's often a good practice to revalidate or re-fetch the relevant data from the server. This ensures that the client-side state is fully consistent with the server, especially if the server might have applied additional logic or side effects not fully captured by the optimistic update.&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Stale-While-Revalidate (SWR) pattern:&lt;/strong&gt; Libraries like &lt;code&gt;SWR&lt;/code&gt; and &lt;code&gt;react-query&lt;/code&gt; excel here. They show cached (potentially optimistic) data immediately, then fetch fresh data in the background, and update the UI once the fresh data arrives. This combines the best of both worlds: instant feedback and eventual strong consistency.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;the-power-of-immutable-updates&quot;&gt;The Power of Immutable Updates&lt;/h4&gt;
&lt;p&gt;When modifying client-side state, especially within global stores, using immutable update patterns is crucial. Instead of directly mutating objects or arrays, create new copies with the desired changes.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// Bad (mutable update - can lead to hard-to-track bugs)
// state.items[index].status = newStatus;

// Good (immutable update - creates new object, preserving history)
const updatedItems = state.items.map(item =&amp;gt;
  item.id === updatedItemId ? { ...item, status: newStatus } : item
);
// state.items = updatedItems; // In a state management library, this would be dispatched
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Immutability helps in several ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Predictability:&lt;/strong&gt; State changes are explicit and easier to reason about.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Time Travel Debugging:&lt;/strong&gt; Many state management tools (like Redux DevTools) rely on immutable state to allow &quot;time travel&quot; through state changes, which is invaluable for debugging race conditions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Easier Rollback:&lt;/strong&gt; If you have a snapshot of the state &lt;em&gt;before&lt;/em&gt; an optimistic update, reverting is as simple as replacing the current state with that snapshot.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Libraries like Immer (often used with Redux, Zustand) simplify writing immutable updates by allowing you to write &quot;mutating&quot; code that is internally translated into immutable operations.&lt;/p&gt;
&lt;h3 id=&quot;practical-strategies-for-debugging-and-preventing-race-conditions&quot;&gt;Practical Strategies for Debugging and Preventing Race Conditions&lt;/h3&gt;
&lt;p&gt;Identifying and fixing these elusive &quot;fifth click&quot; race conditions requires a combination of proactive design, rigorous testing, and robust monitoring.&lt;/p&gt;
&lt;h4 id=&quot;embrace-network-throttling-and-latency-simulation&quot;&gt;Embrace Network Throttling and Latency Simulation&lt;/h4&gt;
&lt;p&gt;The number one tool for reproducing race conditions is simulating real-world network conditions. Most development environments have near-instantaneous network speeds, masking the very delays that cause these bugs.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Browser Developer Tools:&lt;/strong&gt; Chrome, Firefox, and Edge all offer network throttling capabilities. Set it to &quot;Fast 3G,&quot; &quot;Slow 3G,&quot; or even &quot;Offline&quot; to simulate various conditions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Proxy Tools:&lt;/strong&gt; Tools like Charles Proxy or Fiddler allow fine-grained control over network requests, including introducing artificial delays, simulating dropped connections, or modifying responses.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Toxiproxy:&lt;/strong&gt; A powerful, language-agnostic TCP proxy designed for simulating network conditions (latency, jitter, packet loss, etc.) in automated tests.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By consistently testing under throttled conditions, you force your application to handle the asynchronous nature of network requests and expose race conditions that would otherwise remain hidden.&lt;/p&gt;
&lt;h4 id=&quot;comprehensive-unit-and-integration-testing&quot;&gt;Comprehensive Unit and Integration Testing&lt;/h4&gt;
&lt;p&gt;Your test suite must go beyond simple success/failure scenarios.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Mock API Calls with Delays:&lt;/strong&gt; When testing components that use optimistic UIs, mock your API calls not just for success/failure, but also to introduce artificial delays and out-of-order responses.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// Example using Jest and React Testing Library (conceptual)
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import axios from 'axios'; // Or fetch-mock

jest.mock('axios'); // Mock axios

test('optimistic update handles out-of-order responses correctly', async () =&amp;gt; {
  const item = { id: '1', status: 'inactive' };
  render(&amp;lt;MyOptimisticToggle item={item} /&amp;gt;);

  const toggleButton = screen.getByRole('button', { name: /toggle status/i });

  // Simulate first click: Activate
  axios.put.mockImplementationOnce(() =&amp;gt; new Promise(resolve =&amp;gt; setTimeout(() =&amp;gt; resolve({ data: { status: 'active' } }), 500)));
  fireEvent.click(toggleButton);
  expect(screen.getByText(/status: active/i)).toBeInTheDocument(); // Optimistic update

  // Simulate second click: Deactivate (response comes back faster)
  axios.put.mockImplementationOnce(() =&amp;gt; Promise.resolve({ data: { status: 'inactive' } }));
  fireEvent.click(toggleButton);
  expect(screen.getByText(/status: inactive/i)).toBeInTheDocument(); // Optimistic update

  // Wait for the first (delayed) request to resolve.
  // If not handled correctly, this might revert the status to 'active'
  // even though the user's last action was 'deactivate'.
  await waitFor(() =&amp;gt; {
    // Assert that the UI still reflects the *last successful* user action
    expect(screen.getByText(/status: inactive/i)).toBeInTheDocument();
  }, { timeout: 1000 });

  expect(axios.put).toHaveBeenCalledTimes(2);
});
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Test Sequences of Actions:&lt;/strong&gt; Don't just test single clicks. Write integration tests that simulate multiple rapid clicks, toggles, or submissions, especially those that involve changing the same piece of data. Test scenarios where an intermediate action fails, and subsequent actions succeed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Edge Cases:&lt;/strong&gt; Test network outages (offline mode), server errors (500s), and slow responses.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;monitoring-and-observability&quot;&gt;Monitoring and Observability&lt;/h4&gt;
&lt;p&gt;Even with the best testing, some bugs will slip through. Robust monitoring is your safety net.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Client-Side Error Logging:&lt;/strong&gt; Integrate tools like Sentry, Bugsnag, or LogRocket. These can capture client-side errors, often with stack traces and user context. LogRocket specifically offers session replays, which can be invaluable for understanding the exact sequence of user actions that led to a bug.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Correlation IDs:&lt;/strong&gt; When making API requests, include a &lt;code&gt;correlationId&lt;/code&gt; (or &lt;code&gt;requestId&lt;/code&gt; as discussed earlier) in both the client-side logs and the server-side logs. This allows you to trace a single user action from the frontend, through the network, and into your backend services, making it easier to pinpoint where inconsistencies arise.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;State Snapshots:&lt;/strong&gt; For critical optimistic flows, consider logging snapshots of your client-side state at key points (before optimistic update, after server success, after rollback). This can provide a forensic trail when debugging production issues.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;idempotency-on-the-server-side&quot;&gt;Idempotency on the Server-Side&lt;/h4&gt;
&lt;p&gt;While primarily a backend concern, ensuring server-side operations are idempotent can significantly reduce the impact of client-side race conditions or network retries. An idempotent operation is one that produces the same result regardless of how many times it is executed.&lt;/p&gt;
&lt;p&gt;For example, instead of &lt;code&gt;POST /likes&lt;/code&gt; (which might create duplicate likes if retried), use &lt;code&gt;PUT /likes/{postId}/{userId}&lt;/code&gt; or &lt;code&gt;PATCH /likes/{postId} { action: 'toggle' }&lt;/code&gt; where the server logic handles the state change robustly, even if the request arrives multiple times. This makes the system more tolerant to duplicate requests that might arise from client-side retry logic or race conditions.&lt;/p&gt;
&lt;h3 id=&quot;when-not-to-be-optimistic-trade-offs-and-alternatives&quot;&gt;When Not to Be Optimistic: Trade-offs and Alternatives&lt;/h3&gt;
&lt;p&gt;Optimistic UIs are powerful, but they are not a silver bullet. There are situations where the risks outweigh the benefits.&lt;/p&gt;
&lt;h4 id=&quot;high-stakes-operations&quot;&gt;High-Stakes Operations&lt;/h4&gt;
&lt;p&gt;For actions where accuracy and certainty are paramount, and the consequences of a temporary inconsistency are severe, a pessimistic approach is often safer.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Financial Transactions:&lt;/strong&gt; Transferring money, making payments. Users need absolute confirmation that their action has been processed before seeing a change.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Critical Data Deletion:&lt;/strong&gt; Deleting user accounts, irreplaceable files. An accidental optimistic deletion followed by a failure could lead to confusion and data loss concerns.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Security-Sensitive Operations:&lt;/strong&gt; Changing passwords, updating permissions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In these scenarios, a brief loading spinner or a confirmation dialog is a small price to pay for ensuring the user's confidence and data integrity.&lt;/p&gt;
&lt;h4 id=&quot;complex-interdependent-state&quot;&gt;Complex Interdependent State&lt;/h4&gt;
&lt;p&gt;When an optimistic update triggers a cascade of complex, interdependent state changes across many parts of the application, managing rollbacks and eventual consistency can become incredibly difficult.&lt;/p&gt;
&lt;p&gt;Consider a multi-step wizard where each step's optimistic submission affects subsequent steps' available options or data. If an early step's optimistic update fails after later steps have already been optimistically updated, rolling back correctly without corrupting the entire flow is a monumental task.&lt;/p&gt;
&lt;h4 id=&quot;pessimistic-ui-as-an-alternative&quot;&gt;Pessimistic UI as an Alternative&lt;/h4&gt;
&lt;p&gt;The direct alternative to optimistic UI is &lt;strong&gt;pessimistic UI&lt;/strong&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Definition:&lt;/strong&gt; The UI waits for a definitive response from the server &lt;em&gt;before&lt;/em&gt; updating its state. A loading indicator is typically displayed during the network request.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pros:&lt;/strong&gt; Simpler to implement, inherently safer, guarantees consistency (what you see is what the server confirmed).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cons:&lt;/strong&gt; Slower perceived performance, can feel less responsive, loading spinners can be frustrating.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Hybrid Approaches:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Often, the best solution is a hybrid.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Optimistic for common, low-risk actions:&lt;/strong&gt; Liking posts, checking checkboxes, adding items to a cart (where a temporary inconsistency is not catastrophic).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pessimistic for critical, high-risk actions:&lt;/strong&gt; Financial transactions, user account management.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Optimistic with Revalidation for complex data:&lt;/strong&gt; Optimistically update, but immediately trigger a background re-fetch of the data to ensure strong consistency once the server responds. This is the core of libraries like &lt;code&gt;react-query&lt;/code&gt; and &lt;code&gt;SWR&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;conclusion-1&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Optimistic UIs are a testament to the ongoing quest for superior user experience in web development. They empower applications to feel incredibly fast and responsive, bridging the gap of network latency with intelligent client-side prediction. However, this power comes with a significant responsibility: meticulously managing the inherent asynchronous challenges and the principle of eventual consistency.&lt;/p&gt;
&lt;p&gt;The elusive &quot;fifth click&quot; race condition serves as a potent reminder that the devil is often in the details – the specific sequence of user actions, network delays, and server responses that can expose fundamental flaws in state management. Building resilient optimistic UIs demands a proactive approach: unique request identification, robust rollback and revalidation mechanisms, immutable state updates, rigorous testing with network simulation, and comprehensive monitoring.&lt;/p&gt;
&lt;p&gt;By understanding the trade-offs and strategically applying these advanced techniques, developers can harness the full potential of optimistic UIs, delivering seamless, high-performance applications that delight users without sacrificing data integrity or reliability. The race for a perfect user experience is ongoing, and with careful engineering, we can ensure our optimistic UIs always finish strong.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/23206/optimistic-uis-navigating-the-race-for-seamless-user-experience</guid>
<pubDate>Wed, 22 Jul 2026 10:01:57 +0000</pubDate>
</item>
<item>
<title>BREAKING NEWS - AI Vs Nation</title>
<link>https://coderlegion.com/11634/breaking-news-ai-vs-nation</link>
<description>&lt;h3 id=&quot;the-next-cyber-conflict-won-t-start-with-soldiers-it-will-start-with-algorithms&quot;&gt;The Next Cyber Conflict Won’t Start With Soldiers — It Will Start With Algorithms&lt;/h3&gt;
&lt;p&gt;On &lt;strong&gt;February 11, 2026&lt;/strong&gt;, the &lt;strong&gt;U.S. Cybersecurity and Infrastructure Security Agency (CISA)&lt;/strong&gt; issued a stark warning to critical infrastructure operators: a recent cyberattack on an overseas energy grid revealed just how vulnerable the systems that power modern society really are.&lt;/p&gt;
&lt;p&gt;In late December 2025, threat actors targeted energy facilities in &lt;strong&gt;Poland&lt;/strong&gt;, gaining access through insecure, internet-facing devices and deploying destructive tools that &lt;strong&gt;damaged remote terminal units (RTUs)&lt;/strong&gt; and &lt;strong&gt;wiped human-machine interface (HMI) control data&lt;/strong&gt;. While the incident didn’t lead to a blackout, it exposed a dangerous truth — &lt;strong&gt;the backbone of national infrastructure is now under digital siege&lt;/strong&gt;. &lt;/p&gt;
&lt;h4 id=&quot;operational-technology-under-attack&quot;&gt;Operational Technology Under Attack&lt;/h4&gt;
&lt;p&gt;Operational technology (OT) — the hardware and software that controls physical processes in power, telecom, manufacturing, and transportation systems — has traditionally been isolated and secure by obscurity. But that isolation is dissolving. As digital networks expand and legacy systems stay online past their support lifecycle, OT is being exposed to threats once limited to business IT networks. &lt;/p&gt;
&lt;p&gt;Threat actors in this latest attack didn’t need to physically breach a power plant or substation. Instead, they exploited &lt;strong&gt;weak credentials, outdated edge devices, and poor network segmentation&lt;/strong&gt; — long-standing issues in many OT environments — to infiltrate and manipulate control systems. ([IT Pro][3])&lt;/p&gt;
&lt;h4 id=&quot;why-this-matters&quot;&gt;Why This Matters&lt;/h4&gt;
&lt;p&gt;These attacks are not random acts of cybercrime. They are &lt;strong&gt;strategic, automated operations&lt;/strong&gt; designed to disrupt — or destroy — the very systems that keep society running. And &lt;strong&gt;as artificial intelligence becomes more capable&lt;/strong&gt;, attackers are increasingly leveraging machine-speed techniques that can scale across entire sectors.&lt;/p&gt;
&lt;p&gt;That means the days of slow, manual cyberattacks are over. Vulnerabilities can be discovered and exploited by autonomous tools in minutes, not hours. Defense can no longer rely on human operators alone. &lt;strong&gt;Machine-speed attacks require machine-speed defenses&lt;/strong&gt; — systems that can detect, respond, and adapt in real time. ([IT Pro][3])&lt;/p&gt;
&lt;h4 id=&quot;beyond-blackouts-a-new-era-of-conflict&quot;&gt;Beyond Blackouts — A New Era of Conflict&lt;/h4&gt;
&lt;p&gt;Cyber conflict isn’t just about cutting power or stealing data anymore. It’s about &lt;strong&gt;destabilizing economies, eroding public trust, and undermining national security&lt;/strong&gt; without firing a single traditional weapon. Operational technology systems — power grids, telecom networks, manufacturing control systems — are now frontline targets. ([MDPI][4])&lt;/p&gt;
&lt;p&gt;This means resilience is no longer just a buzzword — it’s a &lt;em&gt;national security imperative&lt;/em&gt;. Companies and governments must rethink how they secure digital and physical infrastructure, moving beyond perimeter defenses to &lt;strong&gt;zero trust architectures, continuous monitoring, and automated response capabilities&lt;/strong&gt;. &lt;/p&gt;
&lt;h4 id=&quot;can-your-ot-defend-itself-&quot;&gt;Can Your OT Defend Itself?&lt;/h4&gt;
&lt;p&gt;The real question for leaders in both public and private sectors isn’t &lt;em&gt;if&lt;/em&gt; another attack will occur — it’s whether your OT environment is prepared to defend itself against one.&lt;/p&gt;
&lt;p&gt;Legacy systems, default passwords, unsupported firmware, and flat networks aren’t just technical liabilities  they’re strategic weaknesses that adversaries can exploit at machine speed. To protect critical infrastructure, organizations must invest in &lt;strong&gt;AI-enhanced security, real-time anomaly detection, and cross-industry collaboration&lt;/strong&gt; to stay ahead of emerging cyber threats. ([Cybernews][2])&lt;/p&gt;
&lt;h4 id=&quot;in-this-era-resilience-is-national-security&quot;&gt;In This Era, Resilience &lt;em&gt;Is&lt;/em&gt; National Security&lt;/h4&gt;
&lt;p&gt;As cyber adversaries blend automation, AI, and strategic intent, the battlefield has shifted. The next major conflict won’t begin with soldiers crossing borders — it will begin with algorithms probing defenses and manipulating control systems.&lt;/p&gt;
&lt;p&gt;In this new era of digital warfare, &lt;strong&gt;resilience isn’t optional — it’s essential&lt;/strong&gt;. Strengthening defenses, modernizing infrastructure, and embracing a proactive security mindset are no longer best practices — they are fundamental requirements for national and economic stability.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Thanks for reading, and stay secure.&lt;/strong&gt;&lt;/p&gt;
</description>
<category>Videos</category>
<guid isPermaLink="true">https://coderlegion.com/11634/breaking-news-ai-vs-nation</guid>
<pubDate>Sat, 14 Feb 2026 11:26:06 +0000</pubDate>
</item>
<item>
<title>Getting Started with State Management in Still.js</title>
<link>https://coderlegion.com/4829/getting-started-with-state-management-in-still-js</link>
<description>&lt;p&gt;As part of modern Web Application, State management cannot be put aside, as they’re not a simple aspect of complement but a very important piece which applications can not live without especially when it comes to modularized frontend.&lt;/p&gt;
&lt;p&gt;See this video for hand-on practice: &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://youtu.be/la_V8f9Z-8o&quot;&gt;Youtube video&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Follow the official documentation: &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://still-js.github.io/stilljs-site/annotation/#inject-use-services-for-global-store-and-http-services&quot;&gt;Still.js Service Doc&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Join the discord channel if you have further questions: &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://discord.gg/qMpXJED5&quot;&gt;Discord channel&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://still-js.github.io/stilljs-site/&quot;&gt;Still.js&lt;/a&gt;&lt;/strong&gt; provides different ways to handle State management (&lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.youtube.com/watch?v=WuhGN4CICcs&quot;&gt;component/local state management video&lt;/a&gt;&lt;/strong&gt;), therefore when it comes to Global State management, it provides with a built-in features called Services thereby making it possible to handle things in a Reactive way. Follow the bellow animated illustration on how it works behind the scenes:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=17091137399618212845&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Component 4 updates the store state, which is propagated to all subscribed components&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What is a Service in &lt;/strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://still-js.github.io/stilljs-site/&quot;&gt;Still.js&lt;/a&gt;****&lt;/p&gt;
&lt;p&gt;This a a piece in the code placed in the most extreme layer in terms of architecture, thereby allowing  external communication through means like API/HTTP. Services also had the ServiceEvent kind of variables that are declared as follow:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { BaseService, ServiceEvent } from &quot;../../@still/component/super/service/BaseService.js&quot;;

//This class will be places in the app/services/ folder
export class BiddingService extends BaseService {
    /** An array with a single country is being assigner */
    countryStore = new ServiceEvent(['Australia']);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://still-js.github.io/stilljs-site/&quot;&gt;Still.js&lt;/a&gt;&lt;/strong&gt;, all services must extend the BaseService class to be injectable into components. Reactive stores have to use the ServiceEvent type. After creating a service, it should be injected using @Inject, and components can then listen to specific data changes from stores (e.g. countryStore from above code):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { ViewComponent } from &quot;../../../@still/component/super/ViewComponent.js&quot;;
import { BiddingService } from '../../service/BiddingService.js';

export class BiddingDisplay extends ViewComponent {

    isPublic = true;

    /** 
     * @Inject
     * @Path services/
     * @type { BiddingService } */
     bService;

     stAfterInit() {
          this.bService.on('load', () =&amp;gt; { //Check service readiness
               //Bellow, it Subscribe to ServiceEvent variable (countryStore)
               this.bService.countryStore.onChange(newValue =&amp;gt; {
                     console.warn(`New country entered the Bid, follow the list: `, newValue);
               });
           });
      }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the above example, we are specifying the path (folder) where the service is located, but this can be defined globally in the App level.&lt;br&gt;
&lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://still-js.github.io/stilljs-site/&quot;&gt;Still.js&lt;/a&gt;&lt;/strong&gt; uses event-based reactive state management, where components subscribe via the onChange event. Any change to the state triggers notifications to all subscribers, regardless of where the change originated.&lt;/p&gt;
&lt;p&gt;Retrieving value from the service is quite straightforward, as we only need to specify the store and put .value and the end as follow:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;getCountryInTheBid() {
    const countryState = this.bService.countryStore.value;
    console.log(`----&amp;gt; Country Store before updating: `, countryState);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Updating a store is also a simple thing, in some cases we have to use some intermediate variables, this is the case of lists, follow the examples:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;addMoreCountry() {

         /** Retrieve the state */
         const countryState = this.bService.countryStore.value;

        /** Updating the store and re-assigning it to the service */
        countryState.push(‘Madagascar’);
        this.bService.countryStore = countryState;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Do not forget to check &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.youtube.com/watch?v=la_V8f9Z-8o&quot;&gt;the video tutrial&lt;/a&gt;;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://still-js.github.io/stilljs-site/&quot;&gt;Still.js&lt;/a&gt;&lt;/strong&gt; uses event-based reactive state management that simplifies setup by requiring only services and ServiceEvent based stores. Components can inject these and implement their own logic, while any centralized logic can be handled in the main component.&lt;/p&gt;
</description>
<category>Java</category>
<guid isPermaLink="true">https://coderlegion.com/4829/getting-started-with-state-management-in-still-js</guid>
<pubDate>Tue, 19 Aug 2025 19:11:06 +0000</pubDate>
</item>
<item>
<title>Building Scalable React Native Apps with Nucleux and Supabase</title>
<link>https://coderlegion.com/4010/building-scalable-react-native-apps-with-nucleux-and-supabase</link>
<description>&lt;p&gt;Combining Supabase's powerful backend services with Nucleux's atomic state management creates a clean, scalable architecture for React Native applications. This guide shows you how to structure authentication and API services using class-based stores that naturally organize your application logic.&lt;/p&gt;
&lt;h3 id=&quot;the-architecture-pattern&quot;&gt;The Architecture Pattern&lt;/h3&gt;
&lt;p&gt;We'll build a three-layer architecture that separates concerns cleanly:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;ApiService&lt;/code&gt;&lt;/strong&gt;: Handles Supabase client configuration and lifecycle&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;SessionStore&lt;/code&gt;&lt;/strong&gt;: Manages authentication state and user sessions  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Auth&lt;/code&gt; Component&lt;/strong&gt;: Provides the user interface for authentication&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This pattern leverages Nucleux's dependency injection to create a clean separation where each layer has a single responsibility.&lt;/p&gt;
&lt;h3 id=&quot;setting-up-the-api-service&quot;&gt;Setting Up the API Service&lt;/h3&gt;
&lt;p&gt;First, create a centralized API service that manages the Supabase client:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;// src/services/ApiService.ts
import AsyncStorage from &quot;@react-native-async-storage/async-storage&quot;;
import { createClient, SupabaseClient } from &quot;@supabase/supabase-js&quot;;
import { Store } from &quot;nucleux&quot;;
import { AppState, NativeEventSubscription, Platform } from &quot;react-native&quot;;
import &quot;react-native-url-polyfill/auto&quot;;

if (
  process.env.EXPO_PUBLIC_SUPABASE_URL === undefined ||
  process.env.EXPO_PUBLIC_SUPABASE_ANON_KEY === undefined
) {
  throw new Error(&quot;Missing critical environment variables&quot;);
}

class ApiService extends Store {
  public apiClient: SupabaseClient;
  private appStateSubscription: NativeEventSubscription;

  constructor() {
    super();

    this.apiClient = createClient(
      process.env.EXPO_PUBLIC_SUPABASE_URL || &quot;&quot;,
      process.env.EXPO_PUBLIC_SUPABASE_ANON_KEY || &quot;&quot;,
      {
        auth: {
          storage: AsyncStorage,
          autoRefreshToken: true,
          persistSession: true,
          detectSessionInUrl: false,
        },
      }
    );

    this.appStateSubscription = AppState.addEventListener(&quot;change&quot;, (state) =&amp;gt; {
      if (state === &quot;active&quot;) {
        this.apiClient.auth.startAutoRefresh();
      } else {
        this.apiClient.auth.stopAutoRefresh();
      }
    });
  }

  destroy() {
    this.appStateSubscription.remove();
    super.destroy();
  }
}

export default ApiService;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Key Benefits:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Centralized Configuration&lt;/strong&gt;: Single place to manage Supabase setup&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lifecycle Management&lt;/strong&gt;: Properly manages token refresh based on app state&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;creating-the-session-store&quot;&gt;Creating the Session Store&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;SessionStore&lt;/code&gt; handles all authentication logic while staying completely decoupled from UI concerns:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;// src/stores/SessionStore.ts
import ApiService from &quot;@/services/ApiService&quot;;
import { Session, Subscription } from &quot;@supabase/supabase-js&quot;;
import { Store } from &quot;nucleux&quot;;

class SessionStore extends Store {
  public session = this.atom&amp;lt;Session | null&amp;gt;(null);
  public initialized = this.atom&amp;lt;boolean&amp;gt;(false);

  private apiService = this.inject(ApiService);
  private authSubscription: Subscription;

  constructor() {
    super();
    
    this.getSession();
    
    this.authSubscription = this.apiService.apiClient.auth.onAuthStateChange(
      (_, session) =&amp;gt; {
        this.setSession(session);
      }
    ).data.subscription;
  }

  private async getSession() {
    try {
      const { data: { session } } = await this.apiService.apiClient.auth.getSession();
      this.setSession(session);
    } finally {
      this.initialized.value = true;
    }
  }

  private setSession(session: Session | null) {
    this.session.value = session;
  }

  public async signIn(email: string, password: string) {
    return this.apiService.apiClient.auth.signInWithPassword({
      email,
      password,
    });
  }

  public async signOut() {
    return this.apiService.apiClient.auth.signOut();
  }

  destroy() {
    this.authSubscription.unsubscribe();
    super.destroy();
  }
}

export default SessionStore;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Architecture Highlights:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Dependency Injection&lt;/strong&gt;: &lt;code&gt;this.inject(ApiService)&lt;/code&gt; automatically provides the API client&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Atomic State&lt;/strong&gt;: Only components using &lt;code&gt;session&lt;/code&gt; or &lt;code&gt;initialized&lt;/code&gt; re-render when these values change&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Event Handling&lt;/strong&gt;: Supabase auth changes automatically update the atomic state&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Clean API&lt;/strong&gt;: Simple methods for sign in/out that any component can use&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;building-the-auth-component&quot;&gt;Building the Auth Component&lt;/h3&gt;
&lt;p&gt;Finally, create a simple authentication component that consumes the session store:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-tsx&quot;&gt;// components/Auth.tsx
import SessionStore from &quot;@/stores/SessionStore&quot;;
import { useStore, useValue } from &quot;nucleux&quot;;
import React, { useState } from &quot;react&quot;;
import { Alert, StyleSheet, Text, TextInput, TouchableOpacity, View } from &quot;react-native&quot;;

export default function Auth() {
  const sessionStore = useStore(SessionStore);
  const session = useValue(sessionStore.session);
  const initialized = useValue(sessionStore.initialized);

  const [email, setEmail] = useState(&quot;&quot;);
  const [password, setPassword] = useState(&quot;&quot;);
  const [loading, setLoading] = useState(false);

  const handleSignIn = async () =&amp;gt; {
    if (!email || !password) {
      Alert.alert(&quot;Error&quot;, &quot;Please fill in all fields&quot;);
      return;
    }

    setLoading(true);
    try {
      const { error } = await sessionStore.signIn(email, password);
      if (error) Alert.alert(&quot;Error&quot;, error.message);
    } finally {
      setLoading(false);
    }
  };

  const handleSignOut = async () =&amp;gt; {
    const { error } = await sessionStore.signOut();
    if (error) Alert.alert(&quot;Error&quot;, error.message);
  };

  if (!initialized) {
    return &amp;lt;Text&amp;gt;Loading...&amp;lt;/Text&amp;gt;;
  }

  if (session) {
    return (
      &amp;lt;View style={styles.container}&amp;gt;
        &amp;lt;Text style={styles.title}&amp;gt;Welcome back!&amp;lt;/Text&amp;gt;
        &amp;lt;Text&amp;gt;Signed in as: {session.user.email}&amp;lt;/Text&amp;gt;
        &amp;lt;TouchableOpacity style={styles.button} onPress={handleSignOut}&amp;gt;
          &amp;lt;Text style={styles.buttonText}&amp;gt;Sign Out&amp;lt;/Text&amp;gt;
        &amp;lt;/TouchableOpacity&amp;gt;
      &amp;lt;/View&amp;gt;
    );
  }

  return (
    &amp;lt;View style={styles.container}&amp;gt;
      &amp;lt;Text style={styles.title}&amp;gt;Sign In&amp;lt;/Text&amp;gt;
      
      &amp;lt;TextInput
        style={styles.input}
        placeholder=&quot;Email&quot;
        value={email}
        onChangeText={setEmail}
        autoCapitalize=&quot;none&quot;
        keyboardType=&quot;email-address&quot;
      /&amp;gt;
      
      &amp;lt;TextInput
        style={styles.input}
        placeholder=&quot;Password&quot;
        value={password}
        onChangeText={setPassword}
        secureTextEntry
      /&amp;gt;
      
      &amp;lt;TouchableOpacity 
        style={[styles.button, loading &amp;amp;&amp;amp; styles.buttonDisabled]} 
        onPress={handleSignIn}
        disabled={loading}
      &amp;gt;
        &amp;lt;Text style={styles.buttonText}&amp;gt;
          {loading ? &quot;Signing In...&quot; : &quot;Sign In&quot;}
        &amp;lt;/Text&amp;gt;
      &amp;lt;/TouchableOpacity&amp;gt;
    &amp;lt;/View&amp;gt;
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, padding: 20, justifyContent: &quot;center&quot; },
  title: { fontSize: 24, fontWeight: &quot;bold&quot;, marginBottom: 20, textAlign: &quot;center&quot; },
  input: { borderWidth: 1, borderColor: &quot;#ddd&quot;, padding: 12, marginBottom: 10, borderRadius: 5 },
  button: { backgroundColor: &quot;#007AFF&quot;, padding: 12, borderRadius: 5, marginTop: 10 },
  buttonDisabled: { opacity: 0.5 },
  buttonText: { color: &quot;white&quot;, textAlign: &quot;center&quot;, fontWeight: &quot;bold&quot; },
});
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;why-this-architecture-works&quot;&gt;Why This Architecture Works&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1. Clear Separation of Concerns&lt;/strong&gt;&lt;br&gt;
Each layer has a single responsibility, making the code easier to test and maintain.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Automatic Reactivity&lt;/strong&gt;&lt;br&gt;
Components automatically re-render when authentication state changes, without manual subscription management.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. Reusable Business Logic&lt;/strong&gt;&lt;br&gt;
The &lt;code&gt;SessionStore&lt;/code&gt; can be used across multiple components without duplicating authentication logic.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. Type Safety&lt;/strong&gt;&lt;br&gt;
TypeScript ensures you catch authentication-related bugs at compile time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5. Easy Testing&lt;/strong&gt;&lt;br&gt;
Each store can be unit tested independently of React components.&lt;/p&gt;
&lt;h3 id=&quot;the-result&quot;&gt;The Result&lt;/h3&gt;
&lt;p&gt;This architecture gives you a robust foundation for React Native apps that need authentication. The &lt;code&gt;SessionStore&lt;/code&gt; becomes the single source of truth for authentication state, while the component layer focuses purely on user interface concerns.&lt;/p&gt;
&lt;p&gt;Want to explore more patterns? Check out the complete &lt;a rel=&quot;nofollow&quot; href=&quot;https://nucleux.dev&quot;&gt;Nucleux documentation&lt;/a&gt; and see how this same approach scales to complex applications with multiple data stores and API services.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Ready to build your next React Native app? Try combining &lt;a rel=&quot;nofollow&quot; href=&quot;https://nucleux.dev&quot;&gt;Nucleux&lt;/a&gt; with &lt;a rel=&quot;nofollow&quot; href=&quot;https://supabase.com&quot;&gt;Supabase&lt;/a&gt; for a powerful, type-safe development experience.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Mobile Dev</category>
<guid isPermaLink="true">https://coderlegion.com/4010/building-scalable-react-native-apps-with-nucleux-and-supabase</guid>
<pubDate>Tue, 15 Jul 2025 03:35:09 +0000</pubDate>
</item>
<item>
<title>Guide to svelte state for react dummies</title>
<link>https://coderlegion.com/3814/guide-to-svelte-state-for-react-dummies</link>
<description>&lt;p&gt;Are you a legacy React developer?&lt;/p&gt;
&lt;p&gt;Have you heard of the chronicles of a once-mystical creature  known as &lt;a rel=&quot;nofollow&quot; href=&quot;https://react.dev/reference/react/Component&quot;&gt;class based components&lt;/a&gt; in React?&lt;/p&gt;
&lt;p&gt;After spending so long exclusively in the React ecosystem — &lt;em&gt;with only brief forays into Vue.js and Angular&lt;/em&gt; — I almost feel confined, as if under house arrest. It's as if React has become a pandemic and developers its captive patients. Attempting to leave this ecosystem — which has admittedly served me well in terms of developer satisfaction and community standards reminiscent of Golang — feels like swimming against the current of &lt;a rel=&quot;nofollow&quot; href=&quot;https://trends.stackoverflow.co/?tags=reactjs%2Cvue.js%2Cangular%2Csvelte%2Cangularjs%2Cvuejs3&quot;&gt;global popularity trends.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.himanshusb.in/static/0317cbd9c9baba406967fcab92973095/fdaf8/fe-tech-stack-popularity.png&quot; alt=&quot;Untitled-2024-05-26-2011.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;Wait, let's pause for a moment… we're drifting from the topic. This isn't an angry rant about why you should abandon React and sail off to svelte land—I'll save those opinions for another day. While I'm not a svelte advocate (having barely dipped my toes in it), there is some evidence in &lt;a rel=&quot;nofollow&quot; href=&quot;https://svelte.dev/blog/virtual-dom-is-pure-overhead&quot;&gt;svelte's blog&lt;/a&gt; and a single &lt;a rel=&quot;nofollow&quot; href=&quot;https://news.ycombinator.com/item?id=37586203&quot;&gt;Hacker News thread&lt;/a&gt; about the modest runtime performance benefits of svelte over React —though a 95% decrease in bundle size is nothing to laugh at.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.himanshusb.in/static/9aa33eb0a6d03d9ef75bfe99018a8745/1631d/svelte-size-meme.jpg&quot; alt=&quot;hbhflwf8qsb91.jpg&quot;&gt;&lt;/p&gt;
&lt;p&gt;The purpose of this article is to provide an exhaustive guide on the dos, don'ts, and gotchas of local component-level state management in svelte. I was recently influenced by &lt;a rel=&quot;nofollow&quot; href=&quot;https://academypublisher.in/2024/10/07/the-yellow-house/&quot;&gt;the yellow house&lt;/a&gt; — while the column explores Van Gogh's artwork and its psychological implications — my takeaways from it gave me the notion (&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.notion.com/&quot;&gt;also&lt;/a&gt;, the &lt;em&gt;editor of choice&lt;/em&gt; for writing this article) that drawing parallels from our experiences is the most effective way of learning and moving forward.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.himanshusb.in/static/a0aa446529527922dc4245c2520018b8/0217c/empiricism.png&quot; alt=&quot;empiricism_2x (1).png&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;defining-the-scope&quot;&gt;Defining the scope&lt;/h3&gt;
&lt;p&gt;As much as I'd like to cover the intricacies of event handling, idiomatic design patterns, and dive into the internals of svelte, these topics are already well-documented in &lt;a rel=&quot;nofollow&quot; href=&quot;https://svelte.dev/docs&quot;&gt;svelte's docs&lt;/a&gt; for those willing to take a deep dive. Instead, we'll focus our exploration on answering these key questions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How does state management work  and what are runes?&lt;/li&gt;
&lt;li&gt;❓ How does reactivity work?&lt;/li&gt;
&lt;li&gt;What causes dependencies of state to update?&lt;/li&gt;
&lt;li&gt;How to make custom &lt;code&gt;react-like&lt;/code&gt; hooks in svelte?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let's get started &lt;/p&gt;
&lt;h3 id=&quot;let-s-rune-your-day&quot;&gt;Let’s Rune your day&lt;/h3&gt;
&lt;p&gt;Runes constitute the &lt;strong&gt;primary tools&lt;/strong&gt; for &lt;strong&gt;state management&lt;/strong&gt; in svelte. To quote from the &lt;a rel=&quot;nofollow&quot; href=&quot;https://svelte.dev/docs/svelte/what-are-runes&quot;&gt;official svelte docs&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;If you think of svelte as a language, runes are part of the syntax — they are keywords.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Our exploration will be limited to the following set of runes: &lt;code&gt;$state&lt;/code&gt;, &lt;code&gt;$derived&lt;/code&gt; and &lt;code&gt;$effect&lt;/code&gt;. Starting with a snippet of code, we have the same functionality implemented in svelte and React. The annotations highlight the &lt;strong&gt;1:1 mapping&lt;/strong&gt; between syntax in the two libraries (Even though svelte is a framework, we’ll refer to it as a library for the purposes of this article).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.himanshusb.in/static/78e789e82f564f6ea037456bd325f9b5/b43c0/svelte-react-comp.png&quot; alt=&quot;Contrived example for comparison of reactivity&quot;&gt;&lt;/p&gt;
&lt;p&gt;We’ll break down the code piece by piece to highlight the key differences and potential gotchas, so you’re not caught off guard at work.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt; Runes were introduced in &lt;code&gt;v5&lt;/code&gt; — the latest major version at the time of writing this article. All&lt;br&gt;
  state can be safely migrated to use runes, with the added benefit of improved performance. The&lt;br&gt;
  official migration guide is available &lt;a rel=&quot;nofollow&quot; href=&quot;https://svelte.dev/docs/svelte/v5-migration-guide&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;state-reactivity-using-proxies&quot;&gt;State reactivity using proxies&lt;/h3&gt;
&lt;p&gt;In &lt;strong&gt;Fig. 1&lt;/strong&gt;, we declare and initialize a local store using &lt;code&gt;$state(0)&lt;/code&gt;. Under the hood, svelte sets up a &lt;strong&gt;proxy&lt;/strong&gt; to enable reactivity. In simple terms, &lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy&quot;&gt;proxy&lt;/a&gt; intercepts any read or write operations on the state object. The state is consumed in a way that feels similar to React, although updating state in svelte often requires a slight mental shift to get used to.&lt;/p&gt;
&lt;p&gt;In svelte, state is &lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/Mutable&quot;&gt;mutable&lt;/a&gt;, irrespective of it's value being a primitive or an object. This is demonstrated in &lt;strong&gt;Fig. 1&lt;/strong&gt; (&lt;strong&gt;Point 3)&lt;/strong&gt; using &lt;code&gt;upvotes++&lt;/code&gt;. This mutation updates the state and triggers a UI re-render in the next animation frame — similar to React’s &lt;code&gt;setState(upvotes + 1)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Sweet! Let’s move on to other syntax features.&lt;/p&gt;
&lt;h3 id=&quot;deprecating-in-favor-of-derived-amp-effect&quot;&gt;Deprecating &lt;code&gt;$:&lt;/code&gt; in favor of &lt;code&gt;$derived&lt;/code&gt; &amp;amp; &lt;code&gt;$effect&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;In svelte, any variables or functions defined &lt;strong&gt;outside of runes&lt;/strong&gt; are declared and run exactly once when the &lt;strong&gt;component is initialized&lt;/strong&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const upvotes = $state({
  count: 0,
  completed: false,
});
let derivedDoubled = $derived(upvotes.count * 2);
let doubled = upvotes.count;

$effect(() =&amp;gt; {
  let timeout = setInterval(() =&amp;gt; {
    console.log(upvotes.count);
  });
  return () =&amp;gt; clearInterval(timeout);
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are a &lt;a rel=&quot;nofollow&quot; href=&quot;https://svelte.dev/playground/46af7389a13c4f70ad5c751cb1228715&quot;&gt;few points to note from the snippet&lt;/a&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;$effect&lt;/code&gt; — acts as a drop-in replacement for &lt;code&gt;useEffect&lt;/code&gt;, with runtime fine-grained dependency tracking.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;derivedDoubled&lt;/code&gt; uses &lt;code&gt;$derived&lt;/code&gt; — it automatically updates based on variables referenced in the expression &lt;code&gt;upvotes.count * 2&lt;/code&gt;. According to the svelte docs, it updates synchronously using &lt;em&gt;push-pull reactivity&lt;/em&gt;, ensuring that any function accessing derived state receives consistent values in sync with its dependencies.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;doubled&lt;/code&gt;&lt;/strong&gt; — remains unchanged when &lt;code&gt;upvotes.count&lt;/code&gt; updates, behaving similarly to React’s &lt;code&gt;useRef&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;&lt;p&gt; ℹ️ Use &lt;a rel=&quot;nofollow&quot; href=&quot;https://svelte.dev/docs/svelte/$derived#$derived.by&quot;&gt;&lt;code&gt;derived.by(fn)&lt;/code&gt;&lt;/a&gt; for more complex&lt;br&gt;
  logic instead of the shorthand &lt;code&gt;derived(expression)&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;reactivity-is-fine-grained&quot;&gt;Reactivity is fine-grained&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const upvotes = $state({
count: 0,
completed: false
})

$effect(() =&amp;gt; {
console.log(upvotes.completed ? `Voting has completed`: 'Voting not completed')
})

&amp;lt;button onclick={() =&amp;gt; upvotes.count++}&amp;gt;
  clicks: {upvotes.count}
&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://svelte.dev/playground/9f44e80e72f24e0baa2a8d9f798afc35&quot;&gt;In the example above&lt;/a&gt;, we’ve modified the upvotes &lt;code&gt;$state&lt;/code&gt; from &lt;strong&gt;number&lt;/strong&gt; to &lt;strong&gt;object&lt;/strong&gt; . To follow :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We’re incrementing the &lt;code&gt;count&lt;/code&gt; property.&lt;/li&gt;
&lt;li&gt;Using &lt;code&gt;$effect&lt;/code&gt; to log whether the voting has completed — similar to &lt;code&gt;useEffect&lt;/code&gt; in react.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Since only &lt;code&gt;upvotes.completed&lt;/code&gt; is consumed in the &lt;code&gt;$effect&lt;/code&gt; block, it is not executed if &lt;code&gt;upvotes.count&lt;/code&gt; is updated. This check is dynamically done at runtime, similar to useEffect. This is called &lt;code&gt;key&lt;/code&gt; level granularity.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;useEffect(() =&amp;gt; {
  console.log(upvotes.completed ? `Voting has completed` : 'Voting not completed');
}, [upvotes.completed]);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Key Takeaways:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;$effect&lt;/code&gt; &lt;strong&gt;observes the reactive signals&lt;/strong&gt; (&lt;code&gt;$state&lt;/code&gt;, &lt;code&gt;$derived&lt;/code&gt;, etc.) &lt;strong&gt;while executing&lt;/strong&gt; its expression. That means dependencies are dynamically detected during the function call—not inferred during build.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Prevent unncessary re-renders&lt;/strong&gt; — The child components consuming &lt;code&gt;upvotes.completed&lt;/code&gt; will not be re-rendered due to shallow comparison of props.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;React offers better visibility into reactive statement while svelte works for the user with&lt;br&gt;
cleaner syntax and implicitly takes care of the dependencies.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt; ℹ️ Svelte optimizes away redundant assignments such as &lt;code&gt;upvotes.count = upvotes.count&lt;/code&gt; or&lt;br&gt;
  &lt;code&gt;upvotes.count += 0&lt;/code&gt; will invalidate state updates.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;synchronous-state-updates&quot;&gt;Synchronous state updates&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Task&lt;/strong&gt; — We want to execute &lt;code&gt;fetchCountLogs&lt;/code&gt; immediately on update of &lt;code&gt;upvotes.counts&lt;/code&gt; . In React, we couldn’t directly use the next state due to asynchronous updates. Therefore, we are bound to create a new variable called &lt;code&gt;newUpvotes&lt;/code&gt; which needs to be referenced everywhere if we were to use the updated value of &lt;code&gt;upvotes.count&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.himanshusb.in/static/cf72f95e8d3593e73e9fb6bc85368757/940c5/sync-update.png&quot; alt=&quot;Untitled-2024-05-26-2011.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;svelte takes a different approach, decoupling state updates from batched DOM re-renders. This allows &lt;code&gt;fetchCountLogs&lt;/code&gt; to run immediately after incrementing the value of &lt;code&gt;upvotes.count&lt;/code&gt; , consuming the latest value of the state.&lt;/p&gt;
&lt;h3 id=&quot;overriding-immutable-reactivity-in-derived&quot;&gt;Overriding immutable reactivity in &lt;code&gt;$derived&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Coming to the fourth point mentioned in &lt;strong&gt;Fig. 1&lt;/strong&gt;, derived state is rarely mutated manually and therefore declared as a &lt;code&gt;const&lt;/code&gt; in 99% of usecases. A rare case, mentioned in &lt;a rel=&quot;nofollow&quot; href=&quot;https://svelte.dev/docs/svelte/$derived#Overriding-derived-values&quot;&gt;svelte docs&lt;/a&gt;, displays the case of optimistic UI updates, where we want to override &lt;code&gt;$derived&lt;/code&gt; manually, hence declaring it as &lt;code&gt;let&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.himanshusb.in/static/0d8b2548345729ddb048196e04dee3b1/2fbbf/svelte-override.png&quot; alt=&quot;svelte-3.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;While it helps make the website feel snappy, it should be used in &lt;strong&gt;rare instances&lt;/strong&gt; and in features which aren’t critical to write failures. &lt;strong&gt;For Example&lt;/strong&gt;, liking a video on youtube can have optimistic updates, since the count is an estimate to begin with while commenting on the post could only be shown as sent when the server request is marked completed.&lt;/p&gt;
&lt;h3 id=&quot;an-alternative-to-custom-react-like-hooks&quot;&gt;An alternative to custom &lt;code&gt;react-like&lt;/code&gt; hooks&lt;/h3&gt;
&lt;p&gt;By now, we already have all the tools required for creating custom hooks using &lt;code&gt;runes&lt;/code&gt;. It’s a straightforward vanilla solution with an uncanny resemblance to react hooks syntax. I’ve implemented a &lt;code&gt;usePrevious&lt;/code&gt; hook which tracks the previous value of the state variable using runes.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.himanshusb.in/static/b3e11c4c3d1257ada578583961a9f1f6/4d383/svelte-hook.png&quot; alt=&quot;Untitled-2024-05-26-2011.png&quot;&gt;&lt;/p&gt;
&lt;p&gt;Working version for the above example is present &lt;a rel=&quot;nofollow&quot; href=&quot;https://svelte.dev/playground/b21a77e18b314f2a85d2a28412ae9d8d&quot;&gt;here&lt;/a&gt;. There’s a bunch of commonly used hooks implemented on this &lt;a rel=&quot;nofollow&quot; href=&quot;https://userunes.com/explore&quot;&gt;website&lt;/a&gt; in both javascript &amp;amp; typescript . Happy hunting :)&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt; It is worth noticing that &lt;code&gt;runes&lt;/code&gt; can only be used in files with extension &lt;code&gt;svelte.{(ts, js)}&lt;/code&gt; .&lt;br&gt;
&lt;img src=&quot;http://&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;what-lies-ahead-&quot;&gt;What lies ahead?&lt;/h3&gt;
&lt;p&gt;Despite having discussed exhaustively about the properties of component state management and reactivity, there’s certain topics necessary to make your life easier working with svelte:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Managing global state using svelte stores (next part in the series, hopefully)&lt;/li&gt;
&lt;li&gt;Updating &lt;em&gt;parent state from child components&lt;/em&gt; leveraging &lt;code&gt;$props&lt;/code&gt; &amp;amp; &lt;code&gt;$bindable&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bypassing deep reactivity&lt;/strong&gt; using &lt;code&gt;$state.raw&lt;/code&gt; &amp;amp; &lt;code&gt;$state.snapshot&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Handling &lt;a rel=&quot;nofollow&quot; href=&quot;https://svelte.dev/playground/bee667bc01d44d4197322007febc3773&quot;&gt;direct DOM mutations&lt;/a&gt; based on state updates using &lt;a rel=&quot;nofollow&quot; href=&quot;https://svelte.dev/docs/svelte/lifecycle-hooks#tick&quot;&gt;tick&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Resetting component and it’s state using &lt;a rel=&quot;nofollow&quot; href=&quot;https://svelte.dev/docs/svelte/key&quot;&gt;key blocks&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I hope this article helped you understand the basics of runes and state management—and instilled some confidence to support your journey toward becoming a &lt;strong&gt;svelte wizard&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Feel free to comment on what you liked, disliked or hated. Or reach out directly on &lt;a rel=&quot;nofollow&quot; href=&quot;https://x.com/_himanshuc3&quot;&gt;Twitter&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;footnotes&quot;&gt;Footnotes&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://thoughtspile.github.io/2023/04/22/svelte-state/&quot;&gt;Vladimir Klepov&lt;/a&gt; Highly recommend his deep dive into svelte reactivity&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://svelte.dev/docs&quot;&gt;Svelte Docs&lt;/a&gt; to get lost in the rabit hole of the framework&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy&quot;&gt;MDN Docs&lt;/a&gt; It's more than just documentation&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://imgs.xkcd.com/comics/dependency.png&quot;&gt;XKCD Comics&lt;/a&gt; While &lt;a rel=&quot;nofollow&quot; href=&quot;https://xkcd.com/2855/&quot;&gt;Empericism&lt;/a&gt; was more apt for the article, one of my favorites is a take on the fragile nature of proprietary projects&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://academypublisher.in/2024/10/07/the-yellow-house/&quot;&gt;Shymanta D, Dyutimitra S.&lt;/a&gt; The Yellow House is one of numerous psychological case studies, distinguished by its clean and concise presentation&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://userunes.com/&quot;&gt;UseRunes&lt;/a&gt; - Explore custom hooks&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Web Dev</category>
<guid isPermaLink="true">https://coderlegion.com/3814/guide-to-svelte-state-for-react-dummies</guid>
<pubDate>Tue, 08 Jul 2025 05:11:31 +0000</pubDate>
</item>
<item>
<title>Nucleux</title>
<link>https://coderlegion.com/3440/nucleux</link>
<description>&lt;h3 id=&quot;the-story-behind-nucleux&quot;&gt;The Story Behind Nucleux&lt;/h3&gt;
&lt;p&gt;Nucleux emerged from a simple frustration: existing React state management solutions either require complex provider setups (like Context API) or cause unnecessary re-renders (like most global state libraries). After trying Redux, Zustand, Jotai, and building custom solutions, I realized developers needed something that combined atomic precision with organizational clarity.&lt;/p&gt;
&lt;h3 id=&quot;how-it-works-1&quot;&gt;How It Works&lt;/h3&gt;
&lt;p&gt;Nucleux uses atomic state management where each piece of state is independent. When you update an atom, only components subscribed to that specific atom re-render - no cascade effects, no provider hell.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;class TodoStore extends Store {
  todos = this.atom([])
  filter = this.atom('all')
  
  addTodo(text) {
    this.todos.value = [...this.todos.value, { id: Date.now(), text }]
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;function TodoApp() {
  const todoStore = useStore(TodoStore)
  const todos = useValue(todoStore.todos)
  
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;button onClick={() =&amp;gt; todoStore.addTodo('New task')}&amp;gt;
        Add Todo
      &amp;lt;/button&amp;gt;
      &amp;lt;ul&amp;gt;
        {todos.map(todo =&amp;gt; (
          &amp;lt;li key={todo.id}&amp;gt;{todo.text}&amp;lt;/li&amp;gt;
        ))}
      &amp;lt;/ul&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;key-features&quot;&gt;Key Features&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Zero Providers&lt;/strong&gt;: Use state anywhere without wrapping components&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Atomic Updates&lt;/strong&gt;: Only subscribed components re-render&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Organized Architecture&lt;/strong&gt;: Class-based stores group related state and methods&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Built-in Persistence&lt;/strong&gt;: Save to localStorage/AsyncStorage automatically&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dependency Injection&lt;/strong&gt;: Stores can easily depend on other stores&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lightweight&lt;/strong&gt;: Only 3.5KB gzipped&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SSR Compatible&lt;/strong&gt;: Works seamlessly with Next.js and server-side rendering&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;three-powerful-hooks&quot;&gt;Three Powerful Hooks&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;useStore(StoreClass)&lt;/code&gt; - Access store methods&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;const todoStore = useStore(TodoStore)
todoStore.addTodo('New task')
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;useValue(atom | StoreClass, 'atomKey')&lt;/code&gt; - Subscribe to specific atoms&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const todos = useValue(todoStore.todos)
// or
const todos = useValue(TodoStore, 'todos')
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;useNucleux(StoreClass)&lt;/code&gt; - Get everything at once&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const { todos, filter, addTodo } = useNucleux(TodoStore)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;get-involved&quot;&gt;Get Involved&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Try it&lt;/strong&gt;: &lt;code&gt;npm install nucleux&lt;/code&gt; or check the &lt;a rel=&quot;nofollow&quot; href=&quot;https://codesandbox.io/p/sandbox/nucleux-react-qw58s4&quot;&gt;live demo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Contribute&lt;/strong&gt;: Visit &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/martyroque/nucleux&quot;&gt;GitHub&lt;/a&gt; for issues and feature requests&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Documentation&lt;/strong&gt;: Full guides at &lt;a rel=&quot;nofollow&quot; href=&quot;https://nucleux.dev&quot;&gt;nucleux.dev&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We're looking for contributors who understand React performance, state management patterns, and developer experience. Whether it's bug fixes, new features, or documentation improvements - all contributions are welcome!&lt;/p&gt;
</description>
<category>Open Source</category>
<guid isPermaLink="true">https://coderlegion.com/3440/nucleux</guid>
<pubDate>Tue, 24 Jun 2025 01:46:34 +0000</pubDate>
</item>
<item>
<title>How Layered Memoization in Nucleux v1.3.0 Eliminates React's Biggest Performance Pitfall</title>
<link>https://coderlegion.com/3441/how-layered-memoization-in-nucleux-v1-3-0-eliminates-reacts-biggest-performance-pitfall</link>
<description>&lt;p&gt;React applications slow down for one primary reason: unnecessary re-renders. You update a user's name, and suddenly your entire shopping cart re-renders. You toggle a modal, and your complex data table rebuilds itself. Sound familiar?&lt;/p&gt;
&lt;p&gt;This cascade effect has plagued React developers for years, leading to complex optimization strategies with &lt;code&gt;React.memo&lt;/code&gt;, &lt;code&gt;useMemo&lt;/code&gt;, and &lt;code&gt;useCallback&lt;/code&gt;. But what if your state management library could eliminate this problem entirely?&lt;/p&gt;
&lt;h3 id=&quot;the-hidden-cost-of-state-updates&quot;&gt;The Hidden Cost of State Updates&lt;/h3&gt;
&lt;p&gt;Consider this common scenario in a typical React app:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// Traditional approach - everything re-renders
function App() {
  const [user, setUser] = useState({ name: 'John', notifications: 0 })
  const [cart, setCart] = useState([])
  const [theme, setTheme] = useState('dark')

  // Changing user.name triggers re-renders in:
  // - UserProfile component
  // - Cart component (unnecessary!)
  // - ThemeToggle component (unnecessary!)
  
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;UserProfile user={user} /&amp;gt;
      &amp;lt;ShoppingCart cart={cart} /&amp;gt;
      &amp;lt;ThemeToggle theme={theme} /&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When &lt;code&gt;user.name&lt;/code&gt; changes, React re-renders every component that receives any piece of this state, even if they don't use the changed data. This becomes exponentially worse as your app grows.&lt;/p&gt;
&lt;h3 id=&quot;atomic-updates-the-first-step&quot;&gt;Atomic Updates: The First Step&lt;/h3&gt;
&lt;p&gt;Nucleux solves this with atomic state management - only components subscribed to specific atoms re-render:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;class AppStore extends Store {
  user = this.atom({ name: 'John', notifications: 0 })
  cart = this.atom([])
  theme = this.atom('dark')
  
  updateUserName(name) {
    this.user.value = { ...this.user.value, name }
  }
}

function UserProfile() {
  const user = useValue(AppStore, 'user')
  // Only this component re-renders when user changes
}

function ShoppingCart() {
  const cart = useValue(AppStore, 'cart')
  // This NEVER re-renders when user changes
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But even atomic updates have a subtle performance issue...&lt;/p&gt;
&lt;h3 id=&quot;the-memoization-problem&quot;&gt;The Memoization Problem&lt;/h3&gt;
&lt;p&gt;What happens when you update a user object with the same values?&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// This triggers a re-render even though nothing actually changed!
store.updateUserName('John') // Same name as before
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;React compares objects by reference, not content. Even if the data is identical, creating a new object triggers unnecessary updates.&lt;/p&gt;
&lt;h3 id=&quot;enter-layered-memoization&quot;&gt;Enter Layered Memoization&lt;/h3&gt;
&lt;p&gt;Nucleux v1.3.0 introduces layered memoization that intelligently prevents updates when values haven't actually changed:&lt;/p&gt;
&lt;h4 id=&quot;shallow-memoization-default-&quot;&gt;Shallow Memoization (Default)&lt;/h4&gt;
&lt;p&gt;Perfect for primitive values and objects you replace entirely:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;class CounterStore extends Store {
  count = this.atom(0) // Automatically uses shallow memoization
  
  increment() {
    this.count.value += 1 // Only updates when value actually changes
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;deep-memoization&quot;&gt;Deep Memoization&lt;/h4&gt;
&lt;p&gt;Ideal for complex objects where you care about content, not reference:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;class UserStore extends Store {
  profile = this.atom(
    { name: 'John', email: '*Emails are not allowed*', preferences: {...} },
    { memoization: { type: 'deep' } }
  )
  
  updateProfile(newProfile) {
    // Only re-renders if the content actually changed
    this.profile.value = newProfile
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;custom-memoization&quot;&gt;Custom Memoization&lt;/h4&gt;
&lt;p&gt;For specialized comparison logic:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;class ProductStore extends Store {
  product = this.atom(
    { id: 1, name: 'Laptop', price: 999.99, lastUpdated: Date.now() },
    {
      memoization: {
        type: 'custom',
        compare: (prev, next) =&amp;gt; 
          prev.id === next.id &amp;amp;&amp;amp; 
          prev.name === next.name &amp;amp;&amp;amp; 
          prev.price === next.price
        // Ignores lastUpdated timestamp
      }
    }
  )
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;real-world-performance-impact&quot;&gt;Real-World Performance Impact&lt;/h3&gt;
&lt;p&gt;Here's a before/after comparison in a typical e-commerce app:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Without Memoization:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;User updates profile → 12 components re-render&lt;/li&gt;
&lt;li&gt;Product price change → 8 components re-render&lt;/li&gt;
&lt;li&gt;Cart item quantity update → 15 components re-render&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;With Layered Memoization:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;User updates profile with same data → 0 components re-render&lt;/li&gt;
&lt;li&gt;Product price change with same price → 0 components re-render&lt;/li&gt;
&lt;li&gt;Cart update with identical items → 0 components re-render&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;beyond-atoms-memoized-derived-state&quot;&gt;Beyond Atoms: Memoized Derived State&lt;/h3&gt;
&lt;p&gt;Derived state benefits from memoization too:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;class TodoStore extends Store {
  todos = this.atom([])
  filter = this.atom('all')
  
  // Won't recalculate if filtered result contains same items
  filteredTodos = this.deriveAtom(
    [this.todos, this.filter],
    (todos, filter) =&amp;gt; todos.filter(t =&amp;gt; t.status === filter),
    { type: 'deep' } // Compares array content, not reference
  )
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;the-bottom-line&quot;&gt;The Bottom Line&lt;/h3&gt;
&lt;p&gt;Layered memoization transforms your React app's performance by:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Eliminating phantom updates&lt;/strong&gt; - No re-renders when data hasn't actually changed&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Optimizing derived state&lt;/strong&gt; - Complex calculations only run when inputs truly differ&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reducing debugging complexity&lt;/strong&gt; - Fewer unexpected re-renders to track down&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Improving user experience&lt;/strong&gt; - Smoother interactions and better responsiveness&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The best part? It works automatically. Set your memoization strategy once, and Nucleux handles the intelligent comparisons behind the scenes.&lt;/p&gt;
&lt;h3 id=&quot;try-it-yourself&quot;&gt;Try It Yourself&lt;/h3&gt;
&lt;p&gt;Want to experience the performance difference? Check out the complete &lt;a rel=&quot;nofollow&quot; href=&quot;https://nucleux.dev/&quot;&gt;documentation&lt;/a&gt; to see how layered memoization can transform your React application's performance.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://nucleux.dev/&quot;&gt;Nucleux&lt;/a&gt; v1.3.0 is available now on npm. Upgrade today and feel the difference intelligent memoization makes in your React applications.&lt;/em&gt;&lt;/p&gt;
</description>
<category>React</category>
<guid isPermaLink="true">https://coderlegion.com/3441/how-layered-memoization-in-nucleux-v1-3-0-eliminates-reacts-biggest-performance-pitfall</guid>
<pubDate>Tue, 24 Jun 2025 01:46:03 +0000</pubDate>
</item>
<item>
<title>The Role of State Management in Building Scalable Front-End Applications</title>
<link>https://coderlegion.com/479/the-role-of-state-management-in-building-scalable-front-end-applications</link>
<description>&lt;h3 id=&quot;importance-of-state-management-concepts-in-front-end&quot;&gt;Importance of state management concepts in Front-End&lt;/h3&gt;
&lt;p&gt;Today’s front-end applications are more complex and powerful than ever before. The days when the logic was handled solely on the back-end, with the front-end serving as a simple data display layer, are long gone. Modern applications are expected to deliver dynamic, interactive, and seamless user experiences, often requiring sophisticated data management. One essential tool for handling this complexity is &lt;strong&gt;state management&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;State management allows developers to build scalable and maintainable applications by organizing and controlling data flows. For developers working with popular frameworks, there are ready-to-use solutions like Redux for React and NgRx for Angular. However, this article will focus on the general principles and concepts of &lt;strong&gt;state management&lt;/strong&gt;, which apply to any tool or framework.&lt;/p&gt;
&lt;h4 id=&quot;what-is-state-management-delving-into-the-theory&quot;&gt;What is State Management: Delving into the theory&lt;/h4&gt;
&lt;p&gt;In simple terms, &lt;strong&gt;“state“&lt;/strong&gt; refers to the data at a particular moment in time — such as user information, UI state, or data retrieved from APIs. This data can either be &lt;strong&gt;&lt;em&gt;local&lt;/em&gt;&lt;/strong&gt; (specific to a component or group of components) or &lt;strong&gt;&lt;em&gt;global&lt;/em&gt;&lt;/strong&gt; (accessible throughout the entire application). &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Local state&lt;/em&gt;&lt;/strong&gt; is used within a particular component to manage its data independently. For instance, imagine a form where fields depend on values from other fields. To handle these dynamic relationships, the &lt;strong&gt;&lt;em&gt;local state&lt;/em&gt;&lt;/strong&gt; can be applied to simplify data management within that form. Conversely, the &lt;strong&gt;&lt;em&gt;global state&lt;/em&gt;&lt;/strong&gt; enables data sharing across the entire application. Take user settings, for example — preferences like theme settings or language choices that should be accessible from any page. &lt;strong&gt;&lt;em&gt;Global state&lt;/em&gt;&lt;/strong&gt; provides centralized access to this data, ensuring consistency across the app.&lt;/p&gt;
&lt;p&gt;Without properly managing this data you are likely to get “spaghetti code“. Additionally, it will help you to reduce code duplication and enhance component communication.&lt;/p&gt;
&lt;h4 id=&quot;pillars-of-state-management-core-concepts&quot;&gt;Pillars of State Management: Core Concepts&lt;/h4&gt;
&lt;p&gt;Regardless of the tool or framework, some essential principles are critical to implementing effective state management.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Firstly&lt;/strong&gt;, there’s the &lt;strong&gt;Single Source of Truth&lt;/strong&gt; principle, which states that all data should be stored and updated in a single location. This approach simplifies data tracking and updates by ensuring there’s only one source for each piece of data, providing consistent access across the application. With centralized data handling, debugging becomes easier, especially in complex applications.&lt;/p&gt;
&lt;p&gt;Another vital principle is &lt;strong&gt;Unidirectional Data Flow&lt;/strong&gt;. This concept mandates that data flows in a predictable manner from a single source and is modified in a controlled way. This greatly reduces the potential for bugs, and provides a predictable structure, making the application’s behavior easier to understand and troubleshoot.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Finally&lt;/strong&gt;, there’s the principle of &lt;strong&gt;Separation of Concerns&lt;/strong&gt;, which recommends separating business logic from UI concerns. By doing so, &lt;strong&gt;state management&lt;/strong&gt; keeps business logic — such as data handling and updates independent from the UI. &lt;strong&gt;&lt;em&gt;Components can focus solely on displaying data, while complex state logic is handled separately&lt;/em&gt;&lt;/strong&gt;. This modular approach leads to a codebase that’s easier to maintain and update.&lt;/p&gt;
&lt;p&gt;These principles provide a foundation for any state management solution. Whether you choose an existing library or decide to build a custom solution, following these core ideas will help keep your application organized and scalable.&lt;/p&gt;
&lt;h4 id=&quot;the-challenges-of-unmanaged-state-what-can-it-cost-to-you&quot;&gt;The Challenges of Unmanaged State: What can it cost to you&lt;/h4&gt;
&lt;p&gt;One of the most common problems is &lt;strong&gt;data inconsistency&lt;/strong&gt;. When the state isn’t centrally managed, data inconsistencies frequently arise. Imagine a scenario where you’re editing content on the same page where it’s displayed. After making changes and pressing “Update“ or “Save“, you expect the displayed data to refresh. However, if there’s no centralized state, you might see outdated content until you refresh the entire page, creating confusion for a user.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Debugging&lt;/strong&gt; also becomes difficult when the state is not centralized. Initially, tracing bugs and errors might seem manageable, but as the application grows, tracking the source of data and understanding where updates occur can quickly become a complex and time-consuming task.&lt;/p&gt;
&lt;p&gt;Lastly, &lt;strong&gt;code duplication&lt;/strong&gt; becomes almost unavoidable without centralized state management. When components independently manage related data, duplications are likely to arise. This can quickly bloat the codebase, making it harder to maintain and increasing the chances of introducing bugs.&lt;/p&gt;
&lt;h4 id=&quot;when-to-use-state-management-tips-for-implementation&quot;&gt;When to Use State Management: Tips for Implementation&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;Not every application requires state management&lt;/strong&gt;, so it’s crucial to evaluate your project’s needs. For simpler applications, such as a single landing page, state management might add unnecessary complexity. However, as soon as your application has shared data across multiple components or requires complex user interactions, state management becomes beneficial.&lt;/p&gt;
&lt;p&gt;If your application has complex user flows — where data needs to be shared across different components or pages — then state management can help you improve maintainability. For simpler projects, though, it can be overkill.&lt;/p&gt;
&lt;p&gt;If you determine that state management is necessary, &lt;strong&gt;start with a simple approach&lt;/strong&gt;. Begin by managing the state locally within individual components and only scale up to a more comprehensive state management solution as the complexity of your application grows. &lt;strong&gt;Avoid overengineering early on&lt;/strong&gt;; it’s best to let your solution evolve naturally alongside your app’s needs.&lt;/p&gt;
&lt;p&gt;As you expand your state management, &lt;strong&gt;keep it as organized as possible&lt;/strong&gt;. Avoid creating one large state object, as this can quickly become difficult to maintain. Instead, break your state into smaller, manageable sections that can be updated independently, helping keep your state clean and manageable.&lt;/p&gt;
&lt;p&gt;Another valuable tip is to &lt;strong&gt;separate your business logic from the UI&lt;/strong&gt;. Place business logic within your state management layer rather than in UI components. This makes your components &lt;strong&gt;easier to reuse&lt;/strong&gt;, as they focus solely on displaying data rather than managing it. This separation also results in &lt;strong&gt;modular&lt;/strong&gt;, &lt;strong&gt;testable code&lt;/strong&gt; that’s easier to debug and extend.&lt;/p&gt;
&lt;h4 id=&quot;conclusion-know-your-tools-and-needs&quot;&gt;Conclusion: Know your tools and needs&lt;/h4&gt;
&lt;p&gt;If your project is expanding, now is the time to evaluate whether your current approach to &lt;strong&gt;state management&lt;/strong&gt; meets your needs. Start &lt;strong&gt;small and simple&lt;/strong&gt;, but don’t hesitate to adopt a more structured solution as your app grows. Explore tools and practices that align with your project’s requirements, and experiment with different techniques to find what works best. The right &lt;strong&gt;state management&lt;/strong&gt; approach will make your application more robust, scalable, and easier to maintain in the long run. Start integrating these principles today and see the positive impact on your development process and overall code quality!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Useful Links:&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://redux.js.org/&quot;&gt;https://redux.js.org/&lt;/a&gt;&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://ngrx.io/guide/store/why&quot;&gt;https://ngrx.io/guide/store/why&lt;/a&gt;&lt;/p&gt;
</description>
<category>JavaScript</category>
<guid isPermaLink="true">https://coderlegion.com/479/the-role-of-state-management-in-building-scalable-front-end-applications</guid>
<pubDate>Mon, 25 Nov 2024 01:11:12 +0000</pubDate>
</item>
</channel>
</rss>