<?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: Gangadharan Venkataraman</title>
    <description>The latest articles on DEV Community by Gangadharan Venkataraman (@gangadharan_venkataraman_).</description>
    <link>https://dev.to/gangadharan_venkataraman_</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%2F4221570%2F0e6f299e-00e8-4424-88da-1c07b1181e7d.png</url>
      <title>DEV Community: Gangadharan Venkataraman</title>
      <link>https://dev.to/gangadharan_venkataraman_</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gangadharan_venkataraman_"/>
    <language>en</language>
    <item>
      <title>How to Read a Latency Budget</title>
      <dc:creator>Gangadharan Venkataraman</dc:creator>
      <pubDate>Mon, 02 Nov 2026 05:41:08 +0000</pubDate>
      <link>https://dev.to/gangadharan_venkataraman_/how-to-read-a-latency-budget-1plf</link>
      <guid>https://dev.to/gangadharan_venkataraman_/how-to-read-a-latency-budget-1plf</guid>
      <description>&lt;center&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%2Fxx09na1fqs10jzffdhne.png" alt="Gangadharan Venkataraman" width="800" height="828"&gt;
  &lt;p&gt;
    &lt;em&gt;Gangadharan Venkataraman&lt;/em&gt;
  &lt;/p&gt;
&lt;/center&gt;

&lt;p&gt;
  &lt;em&gt;
    Your average response time is 80ms and a tenth of your users are having a bad day.
  &lt;/em&gt;
&lt;/p&gt;

&lt;p&gt;Someone hands you a requirement: "the endpoint should respond in under 200 milliseconds."&lt;/p&gt;

&lt;p&gt;That sentence looks like a specification. It isn't one yet. It's missing the two things that make a latency target actionable — which percentile, and how the budget is divided. Without those, you'll ship something that averages 90ms and still generates complaints.&lt;/p&gt;

&lt;h2&gt;
  
  
  Averages hide the thing you care about
&lt;/h2&gt;

&lt;p&gt;Here's a distribution that averages 80ms:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Percentile&lt;/th&gt;
&lt;th&gt;Latency&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;p50&lt;/td&gt;
&lt;td&gt;45ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;p90&lt;/td&gt;
&lt;td&gt;95ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;p99&lt;/td&gt;
&lt;td&gt;340ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;p99.9&lt;/td&gt;
&lt;td&gt;2,100ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Mean: about 80ms. Looks fine on a dashboard.&lt;/p&gt;

&lt;p&gt;Now count users. One in a hundred waits a third of a second. One in a thousand waits over two seconds. If you're serving 10 million requests a day, that's 10,000 people a day on the two-second path.&lt;/p&gt;

&lt;p&gt;And it's worse than it looks, because slow requests aren't randomly distributed across users. They correlate with cache misses, which correlate with new users, large accounts, and unusual queries. The tail isn't a random tenth of a percent. It's often your most valuable customers, consistently.&lt;/p&gt;

&lt;p&gt;So: &lt;strong&gt;always state a percentile.&lt;/strong&gt; "p99 under 200ms" is a specification. "Under 200ms" is a wish.&lt;/p&gt;

&lt;h2&gt;
  
  
  Draw the budget before you write the code
&lt;/h2&gt;

&lt;p&gt;A latency target is a budget, and a budget gets spent. Write down where before you start, because you'll discover the allocation was never going to work far more cheaply on a whiteboard than in load testing.&lt;/p&gt;

&lt;p&gt;A worked example for a 200ms p99 on a recommendation endpoint:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Stage&lt;/th&gt;
&lt;th&gt;Budget&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Network in + TLS&lt;/td&gt;
&lt;td&gt;15ms&lt;/td&gt;
&lt;td&gt;Mostly out of your control&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Auth + request validation&lt;/td&gt;
&lt;td&gt;5ms&lt;/td&gt;
&lt;td&gt;Cache the token check&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Feature fetch&lt;/td&gt;
&lt;td&gt;40ms&lt;/td&gt;
&lt;td&gt;The risky one. Network call to a store&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Model inference&lt;/td&gt;
&lt;td&gt;60ms&lt;/td&gt;
&lt;td&gt;Measure it, don't guess&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Business rules + filtering&lt;/td&gt;
&lt;td&gt;20ms&lt;/td&gt;
&lt;td&gt;Grows quietly over time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Response serialization&lt;/td&gt;
&lt;td&gt;10ms&lt;/td&gt;
&lt;td&gt;Larger than people expect for big payloads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Network out&lt;/td&gt;
&lt;td&gt;15ms&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Headroom&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;35ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That headroom line is not padding you can reclaim later. It's what absorbs a GC pause, a slow DNS resolution, a noisy neighbour on the host. A budget that sums exactly to target is a budget that misses target.&lt;/p&gt;

&lt;p&gt;Rule of thumb: allocate about 80% and leave the rest.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sequential stages add. This is the whole problem.
&lt;/h2&gt;

&lt;p&gt;If your request calls three services one after another, your p99 isn't the p99 of the slowest one. It's worse than that, because you need all three to be fast simultaneously, and they fail to be independently.&lt;/p&gt;

&lt;p&gt;Three services, each with a 1% chance of being slow, chained sequentially: the probability that none is slow is 0.99³, about 97%. You've turned three 1% tails into a 3% tail just by calling them in order.&lt;/p&gt;

&lt;p&gt;Two fixes, and you want both.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Call concurrently where the dependency graph allows it.&lt;/strong&gt; If feature fetch and user profile lookup don't depend on each other, run them together. Sequential cost is the sum; concurrent cost is the maximum. With a 40ms and a 30ms call, that's 70ms versus 40ms, for the price of a &lt;code&gt;Promise.all&lt;/code&gt; or a &lt;code&gt;CompletableFuture&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Most request paths have more available concurrency than they use, because they were written in the order someone thought about the problem rather than in the order the data requires.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Set per-stage timeouts, not just an overall one.&lt;/strong&gt; An overall 200ms timeout means a stage that hangs consumes the entire budget before anything else gets to run. Give each stage its own, sized from its own p99 plus margin, and make sure the sum of your timeouts is less than your overall budget. Otherwise a single slow dependency guarantees a missed SLA rather than a degraded response.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decide what to do when the budget runs out
&lt;/h2&gt;

&lt;p&gt;This is the design decision most teams skip, and it's the one that determines whether degradation is graceful.&lt;/p&gt;

&lt;p&gt;When the feature store is slow, do you:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Wait, and blow the latency target&lt;/li&gt;
&lt;li&gt;Return an error&lt;/li&gt;
&lt;li&gt;Serve a cached or default result and mark the response as degraded&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Option 3 is almost always right for recommendation, ranking and personalization. A slightly worse recommendation delivered in time beats a perfect one that arrives after the user has scrolled past.&lt;/p&gt;

&lt;p&gt;Option 2 is right for anything where a wrong answer costs more than no answer — payments, authorization, fraud decisions above a threshold.&lt;/p&gt;

&lt;p&gt;Option 1 is never right, but it's the default in most codebases, because nobody chose. Whatever you pick, emit it. A &lt;code&gt;degraded: true&lt;/code&gt; field in the response and a counter on the dashboard will tell you how often you're falling back, which is a number most teams don't have and would be alarmed by.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measure where it hurts
&lt;/h2&gt;

&lt;p&gt;Client-side, not server-side. Your server reports the time from "request parsed" to "response written." The user experiences DNS, connection setup, TLS negotiation, transfer and rendering. Those gaps are frequently larger than everything you've optimized.&lt;/p&gt;

&lt;p&gt;Also: measure at the edge, under real concurrency. A latency number from an unloaded staging environment with a warm cache is a number about your staging environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Always say which percentile. p99 or it isn't a target.&lt;/li&gt;
&lt;li&gt;Write the budget down by stage before you build.&lt;/li&gt;
&lt;li&gt;Keep 20% as headroom and don't spend it.&lt;/li&gt;
&lt;li&gt;Run independent calls concurrently. Sequential stages add up fast.&lt;/li&gt;
&lt;li&gt;Per-stage timeouts, summing to less than the overall budget.&lt;/li&gt;
&lt;li&gt;Decide the fallback explicitly, and emit a flag when you take it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The 200ms wasn't the hard part. Deciding what happens at 201 is.&lt;/p&gt;

&lt;h2&gt;
  
  
  About the author
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Gangadharan Venkataraman&lt;/strong&gt; works on architecture for real-time machine learning and decision platforms. He holds an M.S. in Computer Science from the Georgia Institute of Technology and has spent over a decade building large-scale distributed systems and production ML infrastructure.&lt;/p&gt;

&lt;p&gt;LinkedIn: &lt;a href="https://www.linkedin.com/in/ganga5v/" rel="noopener noreferrer"&gt;linkedin.com/in/ganga5v&lt;/a&gt;&lt;/p&gt;

</description>
      <category>performance</category>
      <category>sre</category>
      <category>systemdesign</category>
    </item>
  </channel>
</rss>
