Set Performance Budgets That Stick
- Shawn West
- Jul 30
- 3 min read
Updated: Aug 6
Performance Engineering · Part 10
Performance rots quietly — no single change makes the app slow, but a hundred small regressions over a year do, and by then nobody knows which one to blame. A performance budget stops the rot by making "slower" a build failure, not a surprise. This walks through setting budgets, enforcing them in CI so a regression blocks the PR, and backing them with production monitoring.
You optimized. Now don't let it regress. Performance budgets enforce the gains.
Step 1: Pick the Metrics (5 min)
For a web app:
p95 response time per endpoint
p99 response time
Time to first byte (TTFB)
Bundle size (frontend)
Memory footprint
Cold start time
For a service:
Request latency
Database query time
Background job duration
Memory usage
Pick what matters; 4-6 metrics is plenty.
Step 2: Set Budgets (5 min)
Based on:
User experience research (Google: > 2s = bad)
Business requirements (financial: < 100ms)
Competitive benchmarks
Current performance
Login endpoint: p95 < 500ms
Search: p95 < 800ms
Bundle size (JS): < 200kb gzipped
Memory: < 256MB
Cold start: < 1s
Budgets aren't aspirations; they're commitments.
Step 3: Measure in CI (10 min)
For frontend bundle:
- name: Build
run: npm run build
- name: Check bundle size
run: |
SIZE=$(stat -c%s build/main.js)
if [ $SIZE -gt 204800 ]; then
echo "Bundle $SIZE exceeds 200kb budget"
exit 1
fi
For backend latency, run perf tests in CI:
- name: Run perf test
run: k6 run --vus 10 --duration 1m perf-test.js
- name: Check thresholds
run: |
P95=$(jq '.metrics.http_req_duration.values.p95' summary.json)
if (( $(echo "$P95 > 500" | bc -l) )); then
echo "p95 ${P95}ms exceeds 500ms budget"
exit 1
fi
Step 4: Block PRs That Regress (5 min)
CI check failing = PR can't merge.
For frontend, tools like:
Lighthouse CI: integrates with GitHub Actions; budgets per metric
size-limit: monitors JS bundle size
Pulsar: Java bundle analyzer
Configure once; every PR checked.
Step 5: Production Monitoring (10 min)
CI tests on synthetic workload. Production is real:
# Prometheus alerts
- alert: P95LatencyHigh
expr: |
histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))
> 0.5
for: 10m
If p95 goes above 500ms for 10 minutes, alert.
Step 6: Per-Endpoint Budgets (10 min)
Different endpoints have different budgets:
- alert: SignInLatencyHigh
expr: |
histogram_quantile(0.95,
rate(http_request_duration_seconds_bucket{endpoint="/api/signin"}[5m]))
> 0.3
for: 10m
- alert: SearchLatencyHigh
expr: |
histogram_quantile(0.95,
rate(http_request_duration_seconds_bucket{endpoint="/api/search"}[5m]))
> 0.8
for: 10m
Granular targets per endpoint.
Step 7: User-Centric Metrics (10 min)
Beyond server metrics, real user monitoring (RUM):
Core Web Vitals: LCP, FID, CLS
Time to Interactive
Custom: time to first meaningful render
Tools: Datadog RUM, New Relic Browser, Sentry, custom + analytics.
These capture what users actually experience.
Step 8: Budget Review (5 min)
Periodically (quarterly):
Did we meet budgets last quarter?
Where did we miss?
Should budgets tighten?
Should they loosen?
Budgets evolve with product reality.
Step 9: Performance Test Environments (10 min)
Don't load-test production. Use:
Dedicated perf environment (production-like)
Realistic data volumes
Realistic user behavior
If perf env doesn't match prod, results are misleading.
Maintain it carefully. Schedule perf tests regularly.
Step 10: Document and Celebrate (5 min)
Make budgets visible:
Dashboard showing each metric against budget
Highlight in retrospectives
Celebrate when team meets all budgets
Visibility creates accountability.
When you fail a budget: postmortem-ish review, not blame. What caused it? How to prevent?
What You Just Did
Performance is enforced, not hoped for. CI catches regressions; production alerts catch drift; quarterly reviews keep budgets relevant.
Common Failure Modes
Set budgets; never check. Decoration.
Aspirational budgets. Constantly fail; team ignores.
No perf environment. Test against tiny data; production fails.
One-time set. Budgets from year 1 don't fit year 3.
No customer-centric metrics. Server is fast; users still feel slow.
You're Done
You've completed Path 18 — Performance Engineering. Profiling, N+1, indexes, caching, load testing, flame graphs, query optimization, memory, cold start, budgets.
Recommend Security Engineering Hands-On next — performance and security often trade off.
Continue the Performance Engineering path
Previous — Part 9: Reduce Cold Start Time
Part of the Performance Engineering learning path.


