DEV Community

speed engineer
speed engineer

Posted on

The One Number That Actually Moves Your Latency (And Why Your Team Keeps Optimizing the Wrong Thing)

The problem

A team I worked with once burned a full sprint shaving a service from 9ms down to 6ms. Clean win, nice PR, everyone felt good. Total request latency at p99: unchanged. Not "improved slightly." Unchanged, down to the millisecond.

That service was never the problem. It was just the easiest one to fix — small codebase, one owner, an obvious N+1 query to kill. Meanwhile a lock-heavy write in a shared Postgres table, three hops downstream, was eating 280ms on the same request path, and nobody had touched it in months because it was owned by a different team and looked scary.

This happens constantly, and it has a name that most engineers know from manufacturing and never apply to their own systems: Theory of Constraints. Eli Goldratt's version is blunt — a chain is only as strong as its weakest link, and reinforcing any other link does nothing for the chain's strength. Applied to a request path: your system has exactly one bottleneck at any given moment, and improving anything that isn't the bottleneck is not "a smaller win." It's a rounding error that shows up in your commit history and nowhere else.

Why it happens

Two things make this trap easy to fall into.

First, the bottleneck is usually the least pleasant thing to fix. It's often owned by someone else, wrapped in a lock or a queue you don't fully understand, or requires a schema change instead of a code change. The 9ms service is pleasant. The 280ms lock contention is not. Teams under sprint pressure gravitate toward pleasant.

Second, most latency dashboards show you averages or per-service breakdowns, not the serial chain a single request actually walks through. If service A is 9ms and service B is 280ms but they're graphed on separate panels with separate y-axes, they look like two roughly-equal-sized problems. They are not. One of them is 97% of your controllable latency and the other is noise.

What to do about it

Goldratt's original framework has five steps, and they map onto engineering almost without translation:

  1. Identify the constraint. Don't guess — trace one real request end-to-end (a flame graph, distributed trace, or even manual timestamps at each hop) and rank stages by wall-clock time, not by whose code it is or how ugly it looks.
  2. Exploit the constraint. Before you architect anything new, squeeze the bottleneck itself: can that lock be shortened, that query indexed, that call made async, without touching anything else?
  3. Subordinate everything else to it. This is the step teams skip. If service A finishes in 9ms and immediately has to wait on service B's 280ms lock, optimizing A to 3ms buys you exactly nothing — A was never the pacing item. Stop spending story points there until the constraint moves.
  4. Elevate the constraint. If step 2 isn't enough, this is where you actually add capacity — a read replica, a cache in front of the hot table, breaking the lock's critical section apart, splitting the write path.
  5. Repeat. Once you fix the constraint, a new one appears somewhere else in the chain. This isn't a one-time exercise; it's a loop.

The practical version of step 1, if you don't have distributed tracing yet: pick your ten slowest requests from the last day, and for each one, log the wall-clock time spent in every downstream call. Sum by destination, not by your own service boundary. The bottleneck is almost never where the on-call rotation assumes it is — it's usually invisible precisely because nobody's dashboard is shaped like the actual request path.

Key takeaways

  • A system has one bottleneck at a time; everything else you optimize is a rounding error on the metric that matters.
  • "Easy to fix" and "worth fixing" are unrelated — the bottleneck is often the ugly, shared, poorly-owned piece nobody wants to touch.
  • Before adding capacity anywhere, trace a real request end-to-end and rank stages by actual wall-clock time, not by service ownership.
  • Subordinate step 3 is the one teams skip: stop improving non-bottleneck stages, even when it feels like progress.
  • Fixing the constraint doesn't end the exercise — it just reveals the next one.

Top comments (0)