Keeping Monorepo Coverage Reliable with Codecov Flags and Carryforward
Codecov flags + carryforward let you track per-component coverage in monorepos even when only partial CI runs. A practical config and honest trade-offs.
20 Nov 2025, 19:27 UTC

Problem: One Coverage Number for Everything
In a monorepo, a single Codecov status check often blends coverage from the API, web, and shared packages into one number. A pull request that touches only the backend can block a frontend change, or pass while critical service code drops below the team's standard. The issue isn't Codecov's capability—it's how coverage gets tagged and reused across partial CI runs.
Thesis: Flags + Carryforward Keep Per-Component Coverage Visible
A flag is a label you attach to a coverage report so Codecov groups uploads under a named component. When you pair flags with the carryforward setting, Codecov reuses the most recent coverage for that flag if the current commit does not upload a new report. This suits CI setups where only affected test suites run per commit, yet you still want pull-request coverage views that reflect the full component history.
Flags Tag Coverage by Component
Each flag acts as an independent coverage bucket. If your repo has an api/ package and a web/ package, you can define coverage-api and coverage-web flags. During CI, you upload coverage for only the package whose tests executed: codecov --flag coverage-api or codecov --flag coverage-web. The Codecov UI then shows two separate coverage timelines, each with its own history, rather than one merged series.
Worked Example: Two-Package Monorepo
Imagine a repository with api/ and web/, each with its own test suite. The CI configuration runs tests for the changed package only, then uploads coverage via the Codecov CLI: codecov --flag coverage-api when api tests ran, or codecov --flag coverage-web when web tests ran. In codecov.yml, you enable carryforward and set per-flag thresholds: flags:
- coverage-api
- coverage-web
carryforward: true
status:
- threshold: 90%
flag: coverage-api
- threshold: 80%
flag: coverage-web. When a pull request touches only api/, the UI shows coverage-api with the new delta and coverage-web carried forward from the base. The status check for coverage-api runs against the 90% threshold, while coverage-web runs against 80%. If the carried-forward coverage-web already meets the threshold, the check passes without running the web test suite.
The Stale-Coverage Risk
Carryforward can mask stale coverage. If a component's test suite silently stops running or the upload fails without an error, the old report persists indefinitely. Teams should pair carryforward with a CI guard that fails when an expected flag upload is missing. Codecov provides a missing-report detection mode in newer uploads, or you can add a simple check that asserts each flag was uploaded before the job ends. Supply-chain note: Codecov had a significant security incident in 2021 involving the bash uploader. Teams with supply-chain concerns should pin the current Codecov CLI version and verify checksums rather than piping remote scripts to shell.
Getting Started
- Add two flags to your
codecov.ymlthat match your package boundaries. - Set
carryforward: trueso reports reuse across partial CI runs. - Configure per-flag status thresholds under
status:if you want independent CI gates. - In your CI job, upload coverage with
codecov --flagfor the package whose tests executed. - Open a pull request touching one component and verify in the Codecov UI that the other flag's coverage is carried forward and its status check reflects the configured threshold.
Start with a small repo or a single PR, observe the UI behavior, and adjust thresholds or the carryforward guard once you understand how your team's CI pattern interacts with the feature.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.