Split the monorepo scan into units such as services or packages, work out which units a change affects, scan those in parallel on separate workers, and merge the results under one check. Attribute each finding to an owning team by path. This keeps time to result flat as the repository grows.
The problem
One scan job for a whole monorepo has three problems. It is slow, because it processes everything. It is fragile, because one failure discards all results. And its output is hard to act on, because findings from forty teams land in one list with no owner.
Sharding addresses all three by treating each component as its own scan unit.
Choose the shard unit
| Shard unit | Good for | Watch out for |
|---|---|---|
| Service or application directory | Microservice monorepos | Shared libraries used by many services |
| Package or workspace | Language workspaces with a manifest per package | Hoisted dependencies in a root lockfile |
| Build target | Repositories with a build graph | Needs build tool integration |
| Owner path | Ownership files map teams to directories | Paths with no owner |
Steps
- Define the components in a manifest kept in the repository, each with a path pattern, a scan type list and an owner.
- For a pull request, compute changed files and map them to components.
- Add components that depend on the changed ones, using the dependency graph if you have one. A change to a shared library should trigger scans of its consumers.
- Enqueue one job per affected component, each with its own dedupe key.
- Run the jobs in parallel with a concurrency cap so one pull request cannot occupy the fleet.
- Merge results into one check run on the pull request, grouped by component, with a pass only if all required shards pass.
- On merge to the main branch, run the same sharded scan so the baseline stays current.
Dependencies are the tricky part
Many monorepos use one root lockfile for the whole repository. A change to it touches every component. Treat a lockfile change as a change to all components that resolve from it, but use the inventory diff, not the file diff, to decide whether anything really changed: if the new resolved set for a component is identical, skip its match step.
Shared code needs the same care. If an internal library has a vulnerable dependency, the finding belongs to the library owner, but the consumers are exposed. Report the finding once at its source and link affected consumers.
Keep a full scan
Sharding by changed paths is a form of incremental scanning, so keep a scheduled full scan to catch anything the mapping missed. Compare the full scan's findings with the sum of shard results, and investigate differences.
Ownership and routing
Use the owner field to route findings. A finding with no owner goes to a default queue that someone reviews weekly, and the count of unowned paths is a metric worth tracking.
Try it yourself
Click through it at your own pace, or open it full screen. Watch how reachability narrows a long list to the findings that matter, which helps when a monorepo produces many.
Safeguard's reachability analysis helps separate dependency findings that the code actually uses. You can try the free scan at https://gold.safeguard.sh/scan.
Quick check
Do I need a dependency graph to shard?
Not to start. Path based sharding works, but add the graph when shared libraries cause missed scans of consumers.
Should every shard be required to pass?
Usually yes for affected shards. Unaffected shards are skipped, not failed.
Last reviewed: 19 October 2026.