Counters only go up (except when they reset to 0 on restart).
Graphing raw counter values is rarely useful — you almost never care about the absolute number.
rate() calculates the per-second average rate of increase.
It takes a range vector (a metric with a time window) and returns an instant vector:
rate(http_requests_total{job="demo"}[5m])
This tells you: "how many requests per second, averaged over the last 5 minutes?"
rate() handles counter resets automatically.
If a process restarts and the counter drops to 0, rate() detects the reset and adjusts the calculation so the result stays correct.
Choose the time window carefully.
The window (e.g., [5m]) should be at least 4x the scrape interval to tolerate missed scrapes. For a 15s scrape interval, [1m] is the minimum, but [5m] provides smoother results.
Stick to rate() and per-second units.
Using per-second rates everywhere makes it easier to compare and combine metrics later with arithmetic.