Problem
The average is 240ms. That single number is compatible with every request taking 240ms, and with half of them taking 80ms while the other half take 400ms. The viewer needs to know which world they are in.
Solution
Divide the range into buckets, count what falls in each, draw the counts as bars. The shape that comes out answers questions no summary statistic can: is this one population or two, is it symmetric or does it have a tail, is there a wall at some value where a timeout is truncating everything.
Everything then depends on the buckets, and the buckets are usually somebody’s default. Grafana’s histogram panel defaults to a Bucket count of 30 when left empty, or you set Bucket size directly and leave it blank for automatic sizing at roughly 10% of the full range. There is also a Bucket offset, for when the first bucket shouldn’t start at zero, which only does anything if it is greater than zero and smaller than the bucket size. Too few buckets and two humps merge into one. Too many and the shape dissolves into noise. Neither failure announces itself.
On the storage side the same decision has already been made before the panel sees anything. Prometheus classic histograms are counted into fixed buckets chosen at instrumentation time, which is why the quantile estimates that come back out are bounded by bucket width and not by anything about the data. Native histograms change the arrangement by bucketing at a fixed relative resolution, so the resolution follows the magnitude instead of being guessed in advance. If you are choosing bucket boundaries by hand today, that is the thing worth knowing exists.
Grafana also offers Stacking at Off, Normal or 100%, plus Combine series to merge everything into one distribution. Stacked histograms of several series are usually a mistake dressed as a feature: the reader has to compare areas that don’t share a baseline.
Use when
The population is large enough to have a shape, and the question is about that shape. Latency, response size, session length, order value, anything where the tail or a second mode is the story.
Don’t use when
The counts are small, where a histogram is a bar chart with a misleading name. Or when the change over time is the point, in which case the histogram is one frame of a film and a heatmap is the film.
Trade-offs
A histogram is a snapshot and discards time entirely, so a distribution that shifted badly an hour ago looks identical to one that has always been this shape. Bucket choice can manufacture or erase a second mode, and almost no dashboard exposes the bucket definition next to the chart. Comparing two histograms drawn at different scales is a trap readers fall into constantly, because the shapes are comparable-looking even when the axes are not. And a long tail forces a choice between clipping it, which hides the worst cases, and showing it, which squashes the body into the first two bars.
Checklist
- How many buckets, and who chose that number?
- Would a different bucket count merge or split a mode that matters?
- Are the bucket edges shown, and do any of them fall on a value people care about, like a timeout?
- Is the x-axis linear when the data is skewed, and would a log axis show more?
- Is the tail clipped, and does the chart say so?
- Is the y-axis a count or a proportion, and does that match the neighbouring panels?
- If two histograms sit side by side, do they share bucket edges and axis ranges?
- Are these buckets computed at query time or fixed at instrumentation time?
- Does the panel report the sample size?
- Would a heatmap answer the viewer’s actual question by adding time back?
Compare
Grafana exposes bucket count, size and offset as panel options, which is honest about the decision and also means two panels of the same metric can disagree about its shape with nothing on screen to explain why. Prometheus moves the argument upstream: classic histograms fix the buckets at instrumentation time, native histograms hold a relative resolution instead, so the same query returns a usefully different answer depending on which the service emits. Honeycomb keeps raw events rather than pre-aggregating, so a distribution is something you ask for at query time over whatever dimension you just thought of, rather than something you predicted a month ago. Datadog leans on distribution metrics with globally accurate percentiles, which trades the ability to see the shape for the ability to aggregate the summary correctly across hosts.
Related
Heatmap is this chart with time added back, and usually the better answer on an operational page. Percentile summary is the same data reduced to a few numbers, and inherits every bucket problem described here. Ratio and rate is the other normalisation people reach for when a raw count misleads. Stacked composition is what a histogram becomes when it is misused for several series at once. Data table is the fallback for readers who need the counts rather than the picture.