Your CI generates a coverage report every run. But by next month, that report is gone — CI artifacts expire, and with them, any record of what your coverage looked like. You can’t track whether coverage is improving or regressing because there’s no historical data.
The Problem: Coverage Without History
Most teams treat code coverage as a point-in-time check. CI runs, coverage is 74%, the build passes. But that number alone doesn’t answer the questions that matter:
- Is coverage going up or down? A single number doesn’t tell you direction
- When did coverage drop? Without history, you can’t pinpoint the commit that removed tests
- Are new files getting tested? You can’t tell if recent code has coverage without comparing over time
- Is the team hitting coverage goals? Progress requires a baseline and a trend line
Why This Happens
CI artifacts expire. GitHub Actions keeps artifacts 90 days max. Most teams set shorter retention. Once they’re gone, the coverage data is gone.
No built-in trend tracking. Coverage tools (Istanbul, coverage.py, gcov) generate reports for the current run. They don’t store or compare across runs.
Context disappears. A coverage report says utils/parser.ts has 45% coverage. It doesn’t tell you it had 60% last week before someone deleted tests, or that it’s been at 45% for six months while the file tripled in size.
Better: Persistent Coverage Tracking
The fix is to store coverage data from every run and visualize it over time. Every data point builds a picture of where your test suite is heading.
What This Looks Like
- Trend charts showing line, branch, and function coverage over days, weeks, or months
- Regression alerts when coverage drops after a commit
- Per-file breakdown showing which files have the lowest coverage
- Commit linking connecting coverage changes to specific code changes
Setting Up Coverage Tracking with Gaffer
Step 1: Generate LCOV Output
Most test frameworks can output LCOV format:
# JavaScript/TypeScript (Vitest)
vitest run --coverage
# JavaScript/TypeScript (Jest)
jest --coverage --coverageReporters=lcov
# Python
coverage run -m pytest && coverage lcov
# Go
go test -coverprofile=coverage.out ./...Step 2: Upload to Gaffer
Add coverage upload to your CI pipeline alongside test results:
# GitHub Actions
- name: Upload coverage to Gaffer
if: always()
run: |
curl -X POST https://app.gaffer.sh/api/upload \
-H "X-API-Key: ${{ secrets.GAFFER_TOKEN }}" \
-F "files=@coverage/lcov.info" \
-F 'tags={"commitSha":"${{ github.sha }}","branch":"${{ github.ref_name }}"}'Step 3: View Trends
The analytics dashboard shows coverage over time. Spot regressions immediately. Track progress toward goals.
What You Get
Trend Visualization
See line, branch, and function coverage plotted over time. Zoom in on specific date ranges. Compare branches.
Regression Detection
When coverage drops after a commit, you see exactly when it happened and can correlate with the code change that caused it.
Per-File Breakdown
Drill down to see which files have the lowest coverage. Focus testing efforts where they’ll have the most impact instead of chasing an aggregate number.
Commit Correlation
Coverage data is linked to specific commits and branches. See how each PR affects overall coverage before merging.
Supported Formats
Currently in beta with LCOV support — the most widely supported coverage format across JavaScript, TypeScript, Python, Go, and more. Cobertura XML and Istanbul JSON support coming soon.
Read the full documentation →Get Started
Gaffer’s free tier includes coverage tracking. Upload your LCOV data and start building a coverage history.