All articles

Backend

Designing low-latency APIs in Go

The practical choices behind reducing response latency—from profiling and concurrency to caching and database access.

JUN 19, 2026 6 min readBy Aryan Ranjan
AR / Backend

Fast APIs are rarely the result of one clever optimization. They come from removing small, repeated costs across the complete request path.

Measure the path first

Before changing code, break latency into useful boundaries: request parsing, business logic, external calls, database access, serialization, and network time.

Average latency can hide the requests users actually feel, so track p50, p95, and p99 separately.

start := time.Now()
result, err := service.ResolveLocation(ctx, input)
metrics.ObserveLatency("resolve_location", time.Since(start))

Keep concurrency intentional

Goroutines make concurrent work approachable, but unbounded concurrency can move the bottleneck rather than remove it. Use deadlines, cancellation, bounded worker pools, and connection limits that reflect the capacity of downstream services.

Cache stable work

Caching is most useful when the invalidation model is explicit. For location data, we identified responses with high read frequency and relatively infrequent updates, then selected TTLs based on acceptable staleness.

Optimize database access

  • Select only the columns required by the response.
  • Verify indexes against real query plans.
  • Avoid accidental N+1 access patterns.
  • Reuse connections and tune the pool using production evidence.

The final improvement came from several changes working together. Good performance engineering is disciplined measurement followed by careful simplification.