CI TEST SHARD BALANCER · RUNS IN YOUR BROWSER

Split your tests into evenly timed CI shards

Paste the JUnit XML your test runner already writes. See how long your slowest shard is now, how long it could be, how many shards are worth paying for, and the exact test files to put on each shard.

Nothing is uploaded. The report is read in this browser and never leaves your device. It is a heuristic over the timings you give it; real runs vary.

1. Load your timings

Works with pytest (--junitxml), Jest (jest-junit), Go (gotestsum --junitfile), Maven/Gradle and RSpec (rspec_junit_formatter).

Setup seconds is the fixed cost every shard job pays before its first test (checkout, install, cache restore). It is why adding shards stops paying off. “One report per current shard” needs several reports, one from each shard you run today.

Run it in CI

Same engine, zero dependencies, one file. Fails the job when your current split drifts away from an even one, so it does not quietly decay as tests are added:

node cli.mjs split junit.xml --shards 4 --setup-seconds 60
node cli.mjs split shard-*.xml --current-from-reports     # each file = one shard you run today
node cli.mjs split junit.xml --shards 6 --group test --matrix   # GitHub Actions matrix JSON
node cli.mjs split junit.xml --shards 4 --max-imbalance-pct 20   # exit 1 if unbalanced

Get cli.mjs and engine.mjs — Node 18+, nothing to install, no network call, ever. Exit codes: 0 ok, 1 over the imbalance limit, 2 unreadable file or bad option.

Common questions

How do I split tests evenly across CI shards?

Export per-test timings as JUnit XML from a recent run, paste them here, and choose the number of shards. The page treats it as a multiway number-partitioning problem: it places the longest files first on the least-loaded shard, then improves the result by moving and swapping files between shards, and for small suites it searches for the exact optimum. It prints the files for each shard as a runnable command.

Which test reports does it read?

JUnit XML from pytest (--junitxml), Jest through jest-junit, Go through gotestsum or go-junit-report, Maven Surefire and Gradle, and RSpec through rspec_junit_formatter. The format is detected automatically and can be forced.

Why is my slowest shard still slow after balancing?

One test or file may be longer than an even share of the whole suite. It cannot be split across shards, so it sets the floor on wall-clock time no matter how many shards you add. The page flags any such unit and, if you grouped by file, suggests grouping by test instead.

How many shards should I use?

Enter your fixed setup time per shard (checkout, dependency install) and an optional wall-clock target. The page sweeps shard counts, shows slowest shard, wall-clock and billed minutes for each, and suggests the cheapest count that meets the target. Setup time is why more shards stop paying off.

Is my test report uploaded anywhere?

No. The page reads what you paste or load in your browser and makes no network request. The CLI reads local files and makes no network call. There is no model and no randomness, so the same input always gives the same split.

Are the predicted times a guarantee?

No. Test timings vary from run to run and with the machine, so real shards will differ from the prediction. Treat the split as a good starting point and re-run the tool on fresh timings every few weeks.

New to this? Read how to split tests across CI shards. Weighing it against a paid splitting service? See the comparison page. Tests that flip between runs wreck any split, so check for them with the Flaky Test Finder.