When a system falls over under campaign traffic, the reflex is to add compute. It is fast, it is visible, and it is frequently aimed at the wrong resource. In our experience the assumed bottleneck and the actual one differ more often than they match — connection pool exhaustion, lock contention and a single unindexed query have each appeared in work where the team was certain the problem was CPU.
Characterisation is unglamorous: build a load profile from real traffic shape rather than a synthetic ramp, run it against production-like data volumes, and push until something breaks. Finding the limit deliberately in a test costs a few days. Finding it during a campaign costs the campaign.
The assumed bottleneck and the real one were different in most engagements we have taken. Measurement is cheaper than being wrong.
The output should be a written capacity model, not a dashboard screenshot — the traffic level at which each resource becomes the constraint, and what to do at each threshold. That document is what lets a business decide how much reliability it wants to buy, which is ultimately a commercial question rather than a technical one.