Automating Performance in CI/CD
Performance testing delivers the most value when it runs automatically on every change, not as a manual gate before major releases. The "Test After Every Change" principle requires pipeline integration that makes performance results as visible as unit test results.
Where Performance Tests Fit in the Pipeline
Integration Approach: Tool Agnostic
DLT provides multiple integration points that work with any CI/CD system:
| Integration Method | How It Works | Best For |
|---|---|---|
| DLT REST API | Start tests and poll results via HTTP calls | Any CI/CD tool with HTTP support |
| DLT CLI | Command-line interface with IAM/Cognito auth | Pipeline scripts, headless environments |
| Scheduled Tests | Cron-based recurring tests configured in DLT | Nightly regression suites |
| MCP Server | AI-assisted analysis of test results | Developer productivity workflows |
Pipeline Integration Pattern
The general pattern for any CI/CD tool:
Trigger: Pipeline stage invokes DLT CLI or API to start a pre-defined test scenario
Wait: Poll for test completion (DLT CLI supports waiting with timeout)
Evaluate: Compare results against the designated baseline run
Gate: If p99 latency or error rate exceeds threshold, fail the pipeline
Report: Publish results as pipeline artifacts for team review
Concrete Example: GitHub Actions with DLT CLI
This workflow runs a performance gate on every push to main. It starts a DLT scenario, waits for completion, and fails the build if p99 exceeds the baseline by more than 15%.
# .github/workflows/perf-gate.yml name: Performance Gate on: push: branches: [main] jobs: load-test: runs-on: ubuntu-latest permissions: id-token: write # For OIDC auth to AWS contents: read steps: - uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: arn:aws:iam::111122223333:role/dlt-ci-role aws-region: us-east-1 - name: Install DLT CLI run: pip install distributed-load-testing-cli - name: Run load test run: | dlt run \ --test-id "regression-suite" \ --wait \ --timeout 1200 \ --output results.json - name: Evaluate results against baseline run: | # Extract p99 from results and compare to baseline P99=$(jq '.results.avg_lt_p99' results.json) BASELINE_P99=$(jq '.baseline.avg_lt_p99' results.json) THRESHOLD=$(echo "$BASELINE_P99 * 1.15" | bc) if (( $(echo "$P99 > $THRESHOLD" | bc -l) )); then echo "::error::Performance regression detected. p99=${P99}ms exceeds baseline+15%=${THRESHOLD}ms" exit 1 fi echo "Performance gate PASSED. p99=${P99}ms within threshold=${THRESHOLD}ms" - name: Upload results artifact if: always() uses: actions/upload-artifact@v4 with: name: dlt-results path: results.json
Adapt the same pattern for GitLab CI (use script: blocks), Jenkins (use sh steps in a Declarative Pipeline), or AWS CodePipeline (invoke DLT via a Lambda action or CodeBuild step).
Baseline Management
DLT supports designating any test run as a "baseline" for comparison. Establish your baseline strategy:
Run a baseline test on every stable release (tag it in DLT)
Compare every subsequent test against this baseline
Update the baseline only when performance improvements are intentional
Track baseline drift over time to detect gradual degradation
Scheduling Recurring Tests
For the "Always Test" principle, configure DLT scheduled tests:
| Test Cadence | Purpose | Typical Configuration |
|---|---|---|
| Nightly | Catch regressions from daily merges | Full load test, moderate concurrency |
| Weekly | Endurance validation | 4-8 hour soak test at 60% expected peak |
| Pre-event | Validate capacity for planned spikes | Spike test at 150% expected peak |
| Monthly | Stress testing to find new ceilings | Ramp to failure |
Handling Performance Test Failures in the Pipeline
When a performance test fails your gate criteria, the team needs a clear escalation path. Without one, teams either ignore the failure ("just override it") or block releases indefinitely.
| Failure Severity | Response | Example |
|---|---|---|
| Minor (p99 regressed 10-20%) | Investigate, create performance ticket, allow deploy with team lead approval | New logging added 15ms to p99 |
| Moderate (p99 regressed 20-50%) | Block deploy, investigate same day, fix or revert within 24 hours | N+1 query introduced in new feature |
| Critical (p99 regressed >50% or errors >5%) | Revert immediately, root cause analysis before re-deploy | Connection pool misconfiguration |
Automate severity classification in your pipeline. Compare the regression percentage against thresholds and route notifications appropriately: minor failures go to a Slack channel, moderate failures page the team lead, critical failures trigger an automatic revert.
Dealing with Flaky Performance Tests
Performance tests have inherent variance. A 2% latency difference between runs is noise, not signal. Reduce false positives with these techniques:
Run multiple iterations. Execute the same test 3 times and use the median result for gate decisions. DLT scheduled tests support this pattern.
Use statistical significance thresholds. A 5% regression that falls within normal variance (based on historical run data) should not fail the pipeline.
Warm up the environment. Run a 30-second warm-up phase before the measured phase. Exclude warm-up metrics from gate evaluation.
Isolate test infrastructure. Shared staging environments produce inconsistent results. Dedicated performance test environments with consistent instance types eliminate infrastructure variance.
Monitor the test infrastructure itself. If your Fargate tasks are CPU-constrained, results reflect the test harness limit, not the application's actual capacity.
Put It Into Practice
Identify which CI/CD tool your team uses
Deploy the DLT CLI in your pipeline agent/runner (IAM auth recommended for automation)
Create a "smoke" performance test: 50 VUs, 2 minutes, run on every PR merge
Create a "full" performance test: production-scale VUs, 15 minutes, run nightly
Set up pipeline failure criteria: fail if p99 > baseline + 15% or error rate > 1%
Go Deeper
Performance Efficiency Pillar: Process and Culture (Well-Architected)