Engineering field note

A Measurement-First Performance Investigation

A repeatable method for finding backend bottlenecks without optimising the wrong part of the system.

Performance work often starts with an attractive theory: a framework is slow, a query needs an index, or a service should be rewritten. The fastest route to a real improvement is usually less dramatic—measure the request path, identify the dominant constraint, and change one thing at a time.

Define the symptom precisely

“The API is slow” is not a testable problem statement. Establish which operation, percentile, environment, and traffic condition is affected. Average latency can hide a severe tail-latency problem, while a synthetic test may not reproduce the data distribution seen in production.

A useful baseline might include:

  • Requests per second
  • Median, p95, and p99 latency
  • Error and timeout rates
  • CPU, memory, and connection utilisation
  • Database query duration and rows examined
  • Downstream service latency

Record the baseline before changing code. It becomes the reference for deciding whether an optimisation worked.

Follow time through the request

Distributed tracing can divide end-to-end latency among application code, database work, network calls, and queues. When traces are unavailable, add focused timing around major boundaries rather than logging every function.

Once the dominant segment is known, use the appropriate tool. A CPU profile will not explain a request waiting on a connection pool, and a database execution plan will not identify excessive JSON allocation in application code.

Check saturation before utilisation

A resource can become a bottleneck before its average utilisation reaches 100 percent. Connection pools, worker queues, and rate limits often show waiting time or rejected work while host-level CPU still appears healthy.

Monitor queue depth, pool wait duration, throttling, and concurrency alongside resource usage. These signals expose contention that an average dashboard may miss.

Change one constraint at a time

After identifying a bottleneck, form a hypothesis with an expected result. For example: “Adding this composite index should reduce scanned rows and lower p95 query latency under the current read pattern.”

Test with representative data and concurrency. Then compare against the same baseline metrics. Multiple simultaneous changes may produce a faster result, but they make it difficult to understand which change helped and which introduced risk.

Verify the system-level effect

Local improvements can move cost elsewhere. Increasing application concurrency may overload the database; caching may reduce latency while serving stale data; batching may improve throughput while increasing individual request delay.

Observe the full request path after deployment and keep a rollback threshold. Performance is a property of the system under a workload, not a score attached to one function.

A measurement-first process may feel slower than immediately rewriting code. In practice, it prevents weeks of optimising components that were never the limiting factor.

Back to the journal