Performance Budgets
Performance Budgets
Section titled “Performance Budgets”Performance budgets help you catch performance regressions early by setting acceptable thresholds for benchmark results. modestbench makes it easy to enforce performance standards in your development workflow and CI/CD pipelines.
Overview
Section titled “Overview”modestbench supports two types of budgets:
- Absolute budgets - Fixed thresholds (e.g., “must complete in under 10ms”)
- Relative budgets - Comparison against baseline (e.g., “no more than 10% slower than production”)
Budgets can be configured to:
- Fail builds when exceeded (default)
- Warn without failing
- Report results without warnings
Quick Start
Section titled “Quick Start”1. Run Benchmarks & Create Baseline
Section titled “1. Run Benchmarks & Create Baseline”# Run your benchmarksmodestbench run
# Save results as a baselinemodestbench baseline set production-v1.0
# Set as default (optional)modestbench baseline set production-v1.0 --default2. Configure Budgets
Section titled “2. Configure Budgets”Add budgets to modestbench.config.json:
{ "baseline": "production-v1.0", "budgets": { "benchmarks/api.bench.js": { "default": { "parseRequest": { "absolute": { "maxTime": "5ms" }, "relative": { "maxRegression": "10%" } } } } }}3. Run with Budget Checks
Section titled “3. Run with Budget Checks”modestbench runIf budgets are exceeded, modestbench exits with code 1 (fails CI).
Budget Configuration
Section titled “Budget Configuration”Absolute Budgets
Section titled “Absolute Budgets”Set fixed performance thresholds:
{ "budgets": { "your-file.bench.js": { "suite-name": { "task-name": { "absolute": { "maxTime": "10ms", // Max mean execution time "minOpsPerSec": 100000, // Min throughput "maxP99": "50ms" // Max 99th percentile } } } } }}Time format: "10ms", "500us", "5s", "100ns"
Relative Budgets
Section titled “Relative Budgets”Compare against a baseline:
{ "baseline": "production-v1.0", "budgets": { "your-file.bench.js": { "suite-name": { "task-name": { "relative": { "maxRegression": "10%", // or 0.10 "baseline": "custom-baseline" // override default } } } } }}Percentage format: "10%" or 0.10 (decimal)
Combined Budgets
Section titled “Combined Budgets”Use both absolute and relative:
{ "budgets": { "your-file.bench.js": { "suite-name": { "critical-path": { "absolute": { "maxTime": "15ms" // Hard limit }, "relative": { "maxRegression": "5%" // Can't regress more than 5% } } } } }}Wildcard Patterns
Section titled “Wildcard Patterns”Instead of specifying every file, suite, and task individually, you can use wildcard patterns to apply budgets broadly:
Pattern Syntax
Section titled “Pattern Syntax”| Level | Exact Match | Wildcard |
|---|---|---|
| File | "api.bench.js" | "**/*.bench.js" (glob) |
| Suite | "String Operations" | "*" (any suite) |
| Task | "parseJSON" | "*" (any task) |
- Files: Use glob patterns (e.g.,
**/*.bench.js,benchmarks/*.bench.js) - Suites/Tasks: Use
*to match any name
Example: Global Default Budget
Section titled “Example: Global Default Budget”Apply a budget to all benchmarks:
{ "budgets": { "**/*.bench.js": { "*": { "*": { "relative": { "maxRegression": "15%" } } } } }}Example: Stricter Budget for Specific Files
Section titled “Example: Stricter Budget for Specific Files”Override the global default with stricter limits for critical paths:
{ "budgets": { "**/*.bench.js": { "*": { "*": { "relative": { "maxRegression": "15%" } } } }, "benchmarks/critical.bench.js": { "*": { "*": { "relative": { "maxRegression": "5%" } } } } }}Example: Mixed Specificity
Section titled “Example: Mixed Specificity”Combine wildcards with exact matches for fine-grained control:
{ "budgets": { "**/*.bench.js": { "*": { "*": { "relative": { "maxRegression": "20%" } } } }, "api.bench.js": { "Authentication": { "*": { "relative": { "maxRegression": "10%" } }, "login": { "absolute": { "maxTime": "50ms" }, "relative": { "maxRegression": "5%" } } } } }}Precedence Rules
Section titled “Precedence Rules”When multiple patterns match a task, the most specific pattern wins:
- Exact matches always take precedence over wildcards
- File specificity: Exact file > partial glob > full glob
- Suite/task specificity: Exact name >
*wildcard
Specificity scoring:
| Pattern Component | Points |
|---|---|
| Exact file name | +2 |
Glob with specific parts (e.g., **/api/**/*.bench.js) | +1 |
Full glob (**/* or *) | +0 |
| Exact suite name | +1 |
| Exact task name | +1 |
Wildcard (*) | +0 |
Example:
For task api.bench.js/Authentication/login:
| Pattern | Specificity | Result |
|---|---|---|
**/*.bench.js/*/* | 1 | Lowest priority |
api.bench.js/*/* | 2 | Medium priority |
api.bench.js/Authentication/* | 3 | Higher priority |
api.bench.js/Authentication/login | 4 | Wins |
When patterns have equal specificity, the budgets are merged, with later definitions overriding earlier ones.
Baseline Management
Section titled “Baseline Management”Creating Baselines
Section titled “Creating Baselines”# From most recent runmodestbench baseline set my-baseline
# With git metadatamodestbench baseline set v2.0 --commit abc123 --branch main
# Set as defaultmodestbench baseline set v2.0 --default
# From specific runmodestbench baseline set my-baseline --run-id run-2025-10-26-abc123Listing Baselines
Section titled “Listing Baselines”modestbench baseline listOutput:
Found 2 baseline(s):
production-v2.0 (default) Run: run-2025-10-26-abc123 Date: 2025-10-26T12:00:00.000Z Commit: abc123def Branch: main
production-v1.0 Run: run-2025-10-25-xyz789 Date: 2025-10-25T10:00:00.000ZViewing Baseline Details
Section titled “Viewing Baseline Details”modestbench baseline show production-v2.0Deleting Baselines
Section titled “Deleting Baselines”modestbench baseline delete old-baselineAnalyzing History for Budget Suggestions
Section titled “Analyzing History for Budget Suggestions”modestbench can analyze your benchmark history and suggest appropriate budgets:
# Analyze last 10 runsmodestbench baseline analyze
# Analyze last 20 runs with 99% confidencemodestbench baseline analyze --runs 20 --confidence 0.99Output:
Analyzing 10 recent run(s)...
Suggested budgets:
"benchmarks/api.bench.js": { "default": { "parseRequest": { "absolute": { "maxTime": "8ms" } } } }, "benchmarks/db.bench.js": { "default": { "queryUsers": { "absolute": { "maxTime": "25ms" } } } }
Copy these budgets to your modestbench.config.json under "budgets" fieldThe suggested budgets are calculated as mean + 2 * stdDev, covering ~95% of normal variation.
Budget Modes
Section titled “Budget Modes”Control how modestbench handles budget failures:
{ "budgetMode": "fail" // "fail", "warn", or "report"}fail(default): Exit with code 1 when budgets exceededwarn: Display warnings but exit with code 0report: Include budget status in output without warnings
CLI Override
Section titled “CLI Override”modestbench run --budget-mode warnCI/CD Integration
Section titled “CI/CD Integration”GitHub Actions
Section titled “GitHub Actions”PR Comparison
Section titled “PR Comparison”Compare PR performance against base branch:
name: Performance Tests
on: pull_request
jobs: benchmark: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 with: fetch-depth: 0 # Need full history
- uses: actions/setup-node@v3 with: node-version: 18
- run: npm ci
# Create baseline from base branch - name: Benchmark base branch run: | git checkout ${{ github.base_ref }} npm run build modestbench run --quiet modestbench baseline set base git checkout -
# Run PR benchmarks with budget check - name: Benchmark PR run: | npm run build modestbench run --baseline base
- name: Upload results if: always() uses: actions/upload-artifact@v3 with: name: benchmark-results path: .modestbench/Persistent Baselines
Section titled “Persistent Baselines”Use committed baseline file:
- name: Run benchmarks run: modestbench run --baseline production-v1.0
- name: Update baseline (main branch only) if: github.ref == 'refs/heads/main' run: | modestbench baseline set production-v1.0 --default git add .modestbench.baselines.json git commit -m "chore: update performance baseline" git pushGitLab CI
Section titled “GitLab CI”benchmark: script: - npm ci - npm run build - modestbench run artifacts: paths: - .modestbench/CircleCI
Section titled “CircleCI”jobs: benchmark: steps: - checkout - run: npm ci - run: npm run build - run: modestbench run - store_artifacts: path: .modestbench/Best Practices
Section titled “Best Practices”1. Start with Generous Budgets
Section titled “1. Start with Generous Budgets”Don’t set budgets too tight initially. Use baseline analyze to get data-driven suggestions.
2. Use Relative Budgets in CI
Section titled “2. Use Relative Budgets in CI”CI environments are noisy. Relative budgets (e.g., maxRegression: "10%") are more reliable than absolute thresholds.
3. Separate Critical from Non-Critical
Section titled “3. Separate Critical from Non-Critical”Use stricter budgets for critical paths:
{ "budgets": { "auth.bench.js": { "default": { "login": { "relative": { "maxRegression": "5%" } // Strict } } }, "utils.bench.js": { "default": { "formatDate": { "relative": { "maxRegression": "20%" } // Relaxed } } } }}4. Update Baselines Regularly
Section titled “4. Update Baselines Regularly”When you make legitimate performance improvements, update your baselines:
modestbench runmodestbench baseline set production-v2.05. Use Multiple Baselines
Section titled “5. Use Multiple Baselines”Track different versions or configurations:
modestbench baseline set production-v1.0modestbench baseline set production-v2.0modestbench baseline set before-optimization6. Commit Baseline Files
Section titled “6. Commit Baseline Files”Include .modestbench.baselines.json in version control so your team shares baselines.
Troubleshooting
Section titled “Troubleshooting””Baseline not found”
Section titled “”Baseline not found””If you see this error:
Error: Baseline "production-v1.0" not foundCreate the baseline first:
modestbench runmodestbench baseline set production-v1.0Flaky Budget Failures in CI
Section titled “Flaky Budget Failures in CI”CI environments have performance variability. Try:
- Increase regression threshold:
"15%"instead of"10%" - Use
budgetMode: "warn"to monitor without failing - Run more iterations:
--iterations 1000for stable results - Use dedicated CI runners: Self-hosted runners have less noise
Budget Always Fails
Section titled “Budget Always Fails”Check if your budget is reasonable:
# See actual performancemodestbench run --verbose
# Compare to budgetmodestbench baseline show your-baselineAdjust budget if current performance is legitimately slower.
Examples
Section titled “Examples”Example 1: Simple Absolute Budget
Section titled “Example 1: Simple Absolute Budget”{ "budgets": { "utils.bench.js": { "default": { "hashPassword": { "absolute": { "maxTime": "100ms" } } } } }}Example 2: Regression Guard for All Tasks
Section titled “Example 2: Regression Guard for All Tasks”Apply a regression guard to all benchmarks using wildcards:
{ "baseline": "production", "budgets": { "**/*.bench.js": { "*": { "*": { "relative": { "maxRegression": "15%" } } } } }}Example 3: Regression Guard with Overrides
Section titled “Example 3: Regression Guard with Overrides”Apply different thresholds to different areas:
{ "baseline": "production", "budgets": { "**/*.bench.js": { "*": { "*": { "relative": { "maxRegression": "20%" } } } }, "api.bench.js": { "*": { "*": { "relative": { "maxRegression": "10%" } } } }, "utils.bench.js": { "default": { "parseRequest": { "relative": { "maxRegression": "5%" } } } } }}Example 4: Per-Task Budgets
Section titled “Example 4: Per-Task Budgets”{ "budgets": { "api.bench.js": { "default": { "parseSmallRequest": { "absolute": { "maxTime": "1ms" } }, "parseLargeRequest": { "absolute": { "maxTime": "10ms" } } } } }}