<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ziad Mohammed</title>
    <description>The latest articles on DEV Community by Ziad Mohammed (@ziad_mohammed).</description>
    <link>https://dev.to/ziad_mohammed</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3943187%2F77549926-3620-443a-b61f-e18addaf46e4.jpg</url>
      <title>DEV Community: Ziad Mohammed</title>
      <link>https://dev.to/ziad_mohammed</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ziad_mohammed"/>
    <language>en</language>
    <item>
      <title>Rate Limiting: How APIs Control Traffic (and Where It Gets Complicated)</title>
      <dc:creator>Ziad Mohammed</dc:creator>
      <pubDate>Fri, 21 Aug 2026 13:12:53 +0000</pubDate>
      <link>https://dev.to/ziad_mohammed/rate-limiting-how-apis-control-traffic-and-where-it-gets-complicated-4p65</link>
      <guid>https://dev.to/ziad_mohammed/rate-limiting-how-apis-control-traffic-and-where-it-gets-complicated-4p65</guid>
      <description>&lt;p&gt;Rate limiting sounds simple. Count requests. Block the extra ones. But when you actually build it, things get complicated fast. This article explains the main ideas, the algorithms, and the real problems that appear in production.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  What Is a Rate Limiter?
&lt;/h2&gt;

&lt;p&gt;A rate limiter watches incoming requests and decides: allow this one, or block it?&lt;/p&gt;

&lt;p&gt;Every rate limiter needs three things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Limit&lt;/strong&gt;: the maximum number of requests allowed. Example: 100 requests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Window&lt;/strong&gt;: the time period for that limit. Example: per minute.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Identifier&lt;/strong&gt;: how the system knows who is sending the request. Example: the user's ID, API key, or IP address.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So a complete rule looks like: "100 requests per minute per user ID."&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;The identifier matters more than most people think.&lt;/strong&gt; IP address seems like a natural choice, but it breaks down quickly. A company office with 500 employees may share one IP address. A university network does the same. If you use IP as your identifier, you can accidentally block hundreds of innocent users because one person on that network sent too many requests. API keys and user IDs are much better options when you have them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Algorithms
&lt;/h2&gt;

&lt;p&gt;There are five common approaches. Each one works differently and fits different situations.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Token Bucket
&lt;/h3&gt;

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

&lt;p&gt;Think of a bucket that holds tokens. Each user has their own bucket.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The bucket has a maximum size, for example 10 tokens.&lt;/li&gt;
&lt;li&gt;Tokens are added at a fixed rate, for example 1 token per second.&lt;/li&gt;
&lt;li&gt;Each request uses 1 token.&lt;/li&gt;
&lt;li&gt;If the bucket is empty, the request is blocked.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The bucket size and the refill rate are two different settings. The refill rate controls how many requests the user can make over time. The bucket size controls how many they can make at once, in a burst.&lt;/p&gt;

&lt;p&gt;A user with a bucket of 10 and a refill rate of 1 per second can send 10 requests in one second, then 1 request every second after that.&lt;/p&gt;

&lt;p&gt;Bursts are not a problem with this algorithm. They are the point. Use token bucket when your users sometimes send several requests at once and that is normal behavior.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Leaky Bucket
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%21BLk7%21%2Cw_1456%2Cc_limit%2Cf_auto%2Cq_auto%3Agood%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fsubstack-post-media.s3.amazonaws.com%252Fpublic%252Fimages%252F1b79ac84-0b8e-4c15-bee3-ac9918cf37b3_507x873.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%21BLk7%21%2Cw_1456%2Cc_limit%2Cf_auto%2Cq_auto%3Agood%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fsubstack-post-media.s3.amazonaws.com%252Fpublic%252Fimages%252F1b79ac84-0b8e-4c15-bee3-ac9918cf37b3_507x873.png" title="Leaky Bucket Algorithm" alt="Leaky Bucket Algorithm|320" width="507" height="873"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Imagine a bucket with a small hole at the bottom.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Requests come in from the top (like water being poured in).&lt;/li&gt;
&lt;li&gt;Requests are processed at a fixed rate through the hole at the bottom.&lt;/li&gt;
&lt;li&gt;If too many requests arrive at once, the bucket overflows and those requests are dropped.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This always processes requests at the same rate. No matter how many requests arrive, the output is smooth and steady.&lt;/p&gt;

&lt;p&gt;The downside: if a user sends no requests for a while, that idle time is wasted. When they come back, they get no extra allowance for the quiet period. Token bucket saves unused capacity; leaky bucket does not.&lt;/p&gt;

&lt;p&gt;Also, requests wait in the queue instead of getting rejected immediately. For web APIs, a fast rejection is usually better than a long wait.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Fixed Window Counter
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4v0tfb7mi8rzkbtblwsf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4v0tfb7mi8rzkbtblwsf.png" alt="Fixed Window Counter Algorithm" width="689" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Time is divided into fixed blocks. For example, one block per hour.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each block has a request limit: 5 requests per minute.&lt;/li&gt;
&lt;li&gt;The system counts requests in the current block.&lt;/li&gt;
&lt;li&gt;If the count reaches the limit, new requests are blocked.&lt;/li&gt;
&lt;li&gt;When the block ends, the counter resets to zero.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the easiest algorithm to build and understand.&lt;/p&gt;

&lt;p&gt;The known problem: a user can send double their limit in a short time by timing it at the boundary between two blocks. Send 5 requests at 11:59:59, then 5 more at 12:00:01. That is 10 requests in 2 seconds, even though the limit is 5 per minute. This is not a rare edge case. Users who want to send as many requests as possible figure this out.&lt;/p&gt;

&lt;p&gt;Use fixed window when your system can handle that occasional double-burst and you want the simplest possible implementation.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Sliding Window Log
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%21E1rY%21%2Cw_1456%2Cc_limit%2Cf_auto%2Cq_auto%3Agood%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fsubstack-post-media.s3.amazonaws.com%252Fpublic%252Fimages%252F0ae7e7bf-7809-4aba-afbb-5c051ef0b869_728x280.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fsubstackcdn.com%2Fimage%2Ffetch%2F%24s_%21E1rY%21%2Cw_1456%2Cc_limit%2Cf_auto%2Cq_auto%3Agood%2Cfl_progressive%3Asteep%2Fhttps%253A%252F%252Fsubstack-post-media.s3.amazonaws.com%252Fpublic%252Fimages%252F0ae7e7bf-7809-4aba-afbb-5c051ef0b869_728x280.png" title="Sliding Window Log Algorithm" alt="Sliding Window Log Algorithm" width="728" height="280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Instead of resetting a counter every hour, this algorithm keeps a record of the exact time of every request.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When a new request arrives, delete all old records outside the window.&lt;/li&gt;
&lt;li&gt;Count the remaining records.&lt;/li&gt;
&lt;li&gt;If the count is at the limit, block the request.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the most accurate approach. The count is always exact. No boundary problem like fixed window.&lt;/p&gt;

&lt;p&gt;The cost is memory. Every request from every user needs a stored timestamp. At high traffic, this adds up fast. Use this when accuracy is critical and traffic is not too high.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Sliding Window Counter
&lt;/h3&gt;

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

&lt;p&gt;This is a lighter version of the sliding window log. Instead of storing every timestamp, it keeps just two numbers: the count from the previous window and the count from the current window.&lt;/p&gt;

&lt;p&gt;It estimates the real rolling count with this formula:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rolling_count = previous_count × (1 - elapsed_fraction) + current_count
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For example, if 30% of the current hour has passed, it assumes 70% of the previous hour's requests are still inside the rolling window. This is an estimate, not an exact count. The error is usually small.&lt;/p&gt;

&lt;p&gt;This is a good middle ground. More accurate than fixed window, much cheaper in memory than sliding window log. It is a good default for most public APIs.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Happens When a Request Is Blocked
&lt;/h2&gt;

&lt;p&gt;When a request exceeds the limit, there are three ways to respond.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Blocking&lt;/strong&gt;: reject the request immediately with HTTP &lt;code&gt;429 Too Many Requests&lt;/code&gt;. Include a &lt;code&gt;Retry-After&lt;/code&gt; header so the client knows when to try again. This is the simplest and most common approach.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Throttling&lt;/strong&gt;: instead of rejecting, slow the request down. Make it wait before processing. The request eventually succeeds, just with added delay. The downside is that the server still holds the connection open while waiting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Shaping&lt;/strong&gt;: allow the request but give it lower priority. High-traffic users go to the back of the line. Normal users are served first. CDNs use this for users who have exceeded their bandwidth limit.&lt;/p&gt;

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

&lt;p&gt;For most APIs, just use blocking. It is fast, clear, and easy for clients to handle.&lt;/p&gt;

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

&lt;p&gt;Everything above works fine on one server. Most production systems run on multiple servers at the same time.&lt;/p&gt;

&lt;p&gt;If each server keeps its own counter in memory, each server applies the limit independently. With 10 servers and a limit of 100 requests per minute, a user can actually send 1000 requests per minute by spreading their traffic across all 10 servers.&lt;/p&gt;

&lt;p&gt;The fix is to use a shared counter that all servers read and write together. Redis is the standard tool for this. Every request goes to Redis to check and update the counter. Redis handles the counting atomically, meaning two servers cannot read the same number at the same time and both allow a request that should have been blocked.&lt;/p&gt;

&lt;p&gt;The cost is one extra network call on every request. At high traffic, that adds latency.&lt;/p&gt;

&lt;p&gt;There is also a question worth deciding before you go to production: what happens if the rate limiter itself crashes? Two options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fail open&lt;/strong&gt;: all requests pass through while the limiter is down. Users are not affected, but your infrastructure is unprotected.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fail closed&lt;/strong&gt;: all requests are blocked while the limiter is down. Your infrastructure is safe, but real users cannot access your API.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Neither option is wrong. It depends on what you are protecting. Decide this early.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to Put the Rate Limiter
&lt;/h2&gt;

&lt;p&gt;A rate limiter can sit in different places in your system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API gateway&lt;/strong&gt;: sits in front of your entire application. Best for blocking abuse before it ever reaches your servers. One configuration applies to everything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Application middleware&lt;/strong&gt;: lives inside your service. Needed when different endpoints have different limits, or when the limit depends on something the gateway does not know about, like a user's subscription plan.&lt;/p&gt;

&lt;p&gt;For most systems: use the gateway for general protection, use application-level limits where the rules are more specific.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which Algorithm Should You Use
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Algorithm&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;th&gt;Main cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Token Bucket&lt;/td&gt;
&lt;td&gt;Bursty but legitimate traffic&lt;/td&gt;
&lt;td&gt;Irregular load on downstream services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Leaky Bucket&lt;/td&gt;
&lt;td&gt;Smooth, predictable output rate&lt;/td&gt;
&lt;td&gt;Wastes idle capacity, slow rejection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fixed Window&lt;/td&gt;
&lt;td&gt;Simplicity matters, bursts are acceptable&lt;/td&gt;
&lt;td&gt;Boundary exploit allows 2x burst&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sliding Window Log&lt;/td&gt;
&lt;td&gt;Maximum accuracy, low traffic&lt;/td&gt;
&lt;td&gt;High memory usage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sliding Window Counter&lt;/td&gt;
&lt;td&gt;Public APIs, balanced accuracy and cost&lt;/td&gt;
&lt;td&gt;Slightly approximate count&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The algorithm matters, but the identifier and placement matter just as much. A good algorithm on the wrong identifier still lets abuse through.&lt;/p&gt;

</description>
      <category>ratelimite</category>
      <category>systemdesign</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Five Caching Strategies, and When Each One Will Burn You</title>
      <dc:creator>Ziad Mohammed</dc:creator>
      <pubDate>Mon, 17 Aug 2026 23:12:33 +0000</pubDate>
      <link>https://dev.to/ziad_mohammed/five-caching-strategies-and-when-each-one-will-burn-you-1kcp</link>
      <guid>https://dev.to/ziad_mohammed/five-caching-strategies-and-when-each-one-will-burn-you-1kcp</guid>
      <description>&lt;p&gt;I have seen Redis added to a system the way people add salt to food, instinctively, before tasting anything. The cache goes in, latency drops, everyone is happy, and then three months later someone files a bug because users are seeing stale data they swear they updated. The cache was the problem. Not because caching is bad, but because the wrong strategy was picked for how the system actually reads and writes.&lt;/p&gt;

&lt;p&gt;There are five patterns worth knowing. They are not interchangeable.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is Cache?
&lt;/h3&gt;

&lt;p&gt;A temporary storage layer that sits between your application and your database. The goal is to avoid repeated expensive database calls by serving frequently accessed data from memory.&lt;/p&gt;

&lt;p&gt;There is no universally correct strategy. The right choice depends on whether your system is read-heavy, write-heavy, or whether data consistency is the priority.&lt;/p&gt;




&lt;h3&gt;
  
  
  1. Cache Aside (Lazy Loading)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F36jtpd5a1sadu963tjqi.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F36jtpd5a1sadu963tjqi.gif" alt="Cache Aside Strategy" width="799" height="169"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flow:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;App checks the cache&lt;/li&gt;
&lt;li&gt;On a hit, return data&lt;/li&gt;
&lt;li&gt;On a miss, app goes to the DB itself, stores the result in cache, returns it&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The app owns all three steps. The cache is passive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On write:&lt;/strong&gt; invalidate or update the cache entry. Invalidation is usually safer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; read-heavy data that does not change constantly (user profiles, product configs, catalog data)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tradeoffs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First request after a miss always pays the full DB cost&lt;/li&gt;
&lt;li&gt;Simultaneous misses on the same key all hit the DB at once (thundering herd)&lt;/li&gt;
&lt;li&gt;Cache-check logic is scattered in application code&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  2. Write Through
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzcvs2qbmrcfyrhz0yes9.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzcvs2qbmrcfyrhz0yes9.gif" alt="Write Through Cache Strategy" width="799" height="169"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flow:&lt;/strong&gt; app writes to cache, cache synchronously writes to DB, response returns only after DB confirms&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; low write frequency with strict read freshness requirements (admin panels, financial dashboards)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tradeoffs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Higher write latency because of two sequential synchronous writes&lt;/li&gt;
&lt;li&gt;Cache pollution: data that is never read still gets cached on every write&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3. Read Through
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fygah1cof4bfl3o0pjkm6.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fygah1cof4bfl3o0pjkm6.gif" alt="Read Through Cache Strategy|720" width="799" height="169"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flow:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;App reads from cache only, never touches DB directly&lt;/li&gt;
&lt;li&gt;On a miss, the cache layer fetches from DB, populates itself, returns data&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The cache owns the DB interaction, not the app. This simplifies application code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; read-heavy workloads where you want to keep DB access out of application code (newsfeeds, product listings)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tradeoffs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Writes typically bypass the cache and go straight to DB, so cached data goes stale immediately after a write&lt;/li&gt;
&lt;li&gt;Staleness window is controlled by TTL, which may not be acceptable for all data types&lt;/li&gt;
&lt;li&gt;Cold start always pays the full DB cost&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  4. Write Back (Write Behind)
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnv3m0ykk0zyh0qlczr3z.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnv3m0ykk0zyh0qlczr3z.gif" alt="Write Back Cache Strategy" width="799" height="169"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flow:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;App writes to cache&lt;/li&gt;
&lt;li&gt;Cache acknowledges success immediately&lt;/li&gt;
&lt;li&gt;Cache flushes to DB asynchronously later&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; write-heavy workloads where throughput matters more than durability (analytics, counters, logging)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tradeoffs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data loss risk if cache crashes before flushing. How much you lose depends on cache persistence config (Redis with no persistence can lose everything since the last flush)&lt;/li&gt;
&lt;li&gt;Not suitable for financial, order, or any data where losing a write is unacceptable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Difference from Write Through:&lt;/strong&gt; Write Back acknowledges before the DB write. Write Through waits for the DB to confirm. Write Back trades durability for speed. Write Through trades speed for consistency.&lt;/p&gt;




&lt;h3&gt;
  
  
  5. Write Around
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh1z68jy3hu64jpkoa0lg.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh1z68jy3hu64jpkoa0lg.gif" alt="Write Around Cache Strategy" width="799" height="169"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Flow:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;App writes directly to DB, cache is skipped entirely&lt;/li&gt;
&lt;li&gt;On read, app checks cache first&lt;/li&gt;
&lt;li&gt;On miss, fetches from DB and populates cache&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Best for:&lt;/strong&gt; data that is written once and read infrequently (audit logs, exported reports, archived records)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tradeoffs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First read after any write is always a cache miss because the cache was never populated at write time&lt;/li&gt;
&lt;li&gt;Increased read latency until the data gets cached through normal read traffic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Difference from Cache Aside:&lt;/strong&gt; Cache Aside can update or invalidate the cache on write. Write Around deliberately skips the cache on every write, assuming the written data will not be read back soon.&lt;/p&gt;




&lt;h3&gt;
  
  
  The summary that actually holds up
&lt;/h3&gt;

&lt;p&gt;Cache Aside and Read Through are for read-heavy systems, but they handle cache population differently and the write behavior of Read Through is easy to overlook. Write Through and Write Back are both write-aware strategies, but they are opposites in terms of what they trade off: Write Through trades write speed for consistency, Write Back trades durability for speed. Write Around is for data you write but rarely read back.&lt;/p&gt;

&lt;p&gt;The mistake is not reaching for one of these. The mistake is not thinking about what your system actually does before picking one.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
