---
title: "Performance and observability"
chapter: "17"
---

# Performance and observability

Performance engineering begins with a measurable user goal and a representative
workload.

## Metrics that matter

Measure throughput, error rate, latency percentiles, allocation rate, heap
occupancy, GC pause/CPU, thread states, executor queues, connection pools, I/O,
and downstream time. Average latency hides slow users.

## Benchmarking

The JVM warms up and optimizes dynamically. Naive loops are often removed or
distorted. Use JMH for microbenchmarks with warmup, forks, measurement
iterations, and results consumption. Validate micro gains in the full system.

## Profiling

Use JFR first for low-overhead production-oriented evidence. Inspect CPU
samples, allocations, locks, file/socket I/O, exceptions, class loading,
virtual-thread pinning, and GC. Use async-profiler or other approved profilers
for deeper views when needed.

## Common improvements

Pick the right algorithm/data structure. Reduce unnecessary I/O and round trips.
Batch safely. Right-size pools. Avoid unbounded queues. Cache only with a
defined key, TTL, invalidation, size, and consistency rule. Reduce allocation
after identifying a real hot path.

## Observability

Use structured logs, metrics, and traces with correlation IDs. Do not use
unique user/order IDs as metric labels. Define SLIs/SLOs for business journeys
and alert on meaningful error-budget burn.

## Capacity and failure

Load-test steady, burst, soak, and degraded dependencies. Performance under
failure can be worse than ordinary load because retries multiply work.

## Feynman check

Benchmarking times one motion in a lab. Profiling watches the whole factory to
find where time and materials are actually spent.
