DEV Community

Hive80-lab
Hive80-lab

Posted on Originally published at hive80-lab.github.io

Capacity Planning for Small Teams: One Page, One Number per Service

Small teams do capacity planning one of two ways: a spreadsheet so elaborate it needs its own owner, or nothing at all until the disk fills on a Sunday. Both fail the same way — neither produces a decision in advance.

What works fits on one page: for each service, the one resource that fails first, its hard limit, the peak it actually hit in the last four weeks, the headroom left, and the pre-agreed action when headroom runs low.

You can read the full note on the HIVE80lab ops-notes site — here is the working version.

The template: one row per service

Service Fails first at Limit 4-wk peak Headroom Weeks left Owner
API (app servers) memory 8 GB 5.2 GB 35% 9 Priya
Postgres primary connections 200 154 23% 5 Marco
Ingest queue queue depth 50k msgs 41k 18% 3 Priya
Primary disk disk 500 GB 415 GB 17% 2 Sam
Background workers CPU 8 vCPU 2.9 64% >12 Sam

The columns, each with a rule:

  • Fails first at — every service has one resource that saturates before the others: CPU, memory, connections, queue depth, disk, a provider rate limit. If you don't know which, you have never watched the service fall over. Latency is not a capacity metric — it is the symptom of the one you missed.
  • Limit — where degradation starts, not the spec sheet. Postgres at 100% connections is dead; at 85% it is timing out under spikes. Set the limit where users feel it.
  • 4-wk peak — p95 of daily peaks, trailing four weeks. Peak, never average: the average hides the daily 4pm spike that is eating your headroom every single day.
  • Weeks left — headroom divided by the weekly growth rate of the peak. This is the only forecast a small team needs; read it like fuel.

The action ladder

A number with no pre-agreed action is trivia.

  • Green (headroom > 40%): nothing. Read the row weekly. Most rows live here forever.
  • Amber (20–40%): schedule the fix this sprint. Pre-agreed moves, in order of preference: delete (kill the job nobody owns), shed (rate-limit or queue), split (move the hot path out), buy (resize up). Buying is last — it hides the current ceiling and leaves the next one in place.
  • Red (< 20%): the fix happens this week, and the row sits on the weekly review agenda until closed.

The ladder matters more than the exact numbers. What it prevents is the monthly meeting where everyone agrees the database is "getting close" and then agrees again next month, slightly louder.

The five traps

Averages instead of peaks. Average CPU of 30% with a daily spike at 95% is not 70% headroom; it is one marketing email away from an incident.

Per-server math for a per-service ceiling. Six autoscaled instances at 40% CPU feels healthy until the connection pool on the single database behind them saturates. The row belongs to the service, not the fleet.

Thresholds with no owner. "We'll keep an eye on it" precedes every saturation outage. A name, a threshold, a move — or the row is decoration.

Planning only after an incident. If the table only gets touched after something falls over, it is a post-mortem artifact, not a plan. Read it in the meeting you already run: five minutes, scan for amber and red.

Forecasting in quarters. Small-team traffic moves like weather, not tides. One enterprise customer with a batch job can double your peak in a week. Plan in weeks-of-headroom, re-read weekly.

Worked example

An eight-person analytics SaaS builds the table on a Tuesday afternoon, pulling peaks from monitoring they already had. Two rows come back red: the ingest queue at 18% headroom growing 6%/week — three weeks left — and the primary disk at 17%. Everything else is green and stays untouched.

The queue takes the buy move: consumers from 2 to 6 (worker CPU was loafing at 35%), peak drops to 22k, headroom back to 56%. Extra cost: $40/month. The disk takes split: attachments move to object storage.

The counter-example is their own previous quarter: "capacity planning" done as a Q4 traffic estimate in a spreadsheet, followed by a Monday-night ingest stall — fourteen hours of backlog, thirty-one apologetic conversations. The stall had been visible in the queue-depth chart for six weeks. Nobody was reading a chart; there was no table, so there was no owner.

From the HIVE80lab kit

Related: golden signals alerting is where the peak numbers come from, the cloud cost spike runbook handles the morning after you buy headroom, and the disk-full incident runbook is what a missed row looks like at 2am.

Top comments (0)