- All playbooks
- Parallelization and sharding
- Run independent checks in parallel
Run independent checks in parallel
Remove false dependencies between lint, build, test, and formatting checks so independent work can start together.
How it works
Identify checks that can run without another check’s output. Give those checks independent execution paths so lint, formatting, builds or tests can start together. Keep dependencies where a job consumes another job’s artifacts, and keep each required result in the final merge decision. The examples below show both splitting sequential work and removing unnecessary job dependencies.
How companies used this technique
Read what each company changed and follow the original sources.
Run build-free checks without waiting for the build
Bun's source-lint tests read only the source tree, yet each test shard ran them after waiting for the full build. They now run in their own GitHub Actions workflow, and the test shards exclude them.
- Use it when
- A check depends on a build job through needs: or runs inside a build-gated job, but never reads the build's output.
- Before applying it
- The moved check stays required, and only checks that never read the build output move.
Run lint beside the build, as its own step
Next.js can lint as part of its build, but that lint runs late in the build. DoorDash decoupled ESLint from the Next.js build and runs its three minutes of linting in parallel with the build, so developers see lint results five minutes sooner. Together with the cobuild fix and the cache change, this cut CI runtime by six minutes and halved billed agent minutes.
Implementing these three changes decreased CI runtime by six minutes and cut billed minutes for agent usage in half. Source
- Use it when
- A build command also runs linting or type checks, so the job waits for both one after the other.
- Before applying it
- Disable the build's own lint only once the separate lint job is a required check, so pull requests stay gated on lint.
Run independent lint and formatting checks together
Mastra scheduled ESLint and formatting checks in one Turbo invocation, applying the parallel-steps recipe through the task runner. The write/autofix commands stayed sequential, and formatting tasks remained uncached.
- Use it when
- Lint and formatting independently inspect the same checkout.
- Before applying it
- Preserve the previous file filters and both exit statuses. Concurrent writers are a different case; keep autofixes ordered and measure CPU contention.
Run independent checks alongside each other
The CLI moved Windows test compilation out of the Linux test script into its own job. The Go SDK split lint into four matrix groups behind a required aggregate.
Windows compilation runs alongside the Linux tests Source
- Use it when
- Independent compilation or analysis tasks share one slow sequential job.
- Before applying it
- Extra jobs repeat setup and use more compute. A passing aggregate must require every child to succeed.
Let tests start without waiting for lint
Tecnix's test legs waited for its basic checks before starting. Shopify/tecnix#74 removes that edge; lint problems still turn the required aggregate basic-checks job red while the much longer test legs run alongside it.
Merged 2026-10-01; the test job on the default branch has no needs edge. Source
- Use it when
- Long jobs wait on another job whose outputs and artifacts they do not use.
- Before applying it
- Keep dependencies that are deliberate gates, such as checks that must pass before a deploy.
Run independent checks at the same time
Translated to GitHub Actions
- What Uber built
- Uber's landing validation ran environment preparation, pre-checks, changed-targets calculation, build and test, and publishing in sequence.
- Your GitHub Actions version
- Run a check that depends on neither the build nor the change calculation in parallel with them.
Uber's landing validation ran environment preparation, pre-checks, CTC, Bazel build and test, and result publishing in sequence. Its P95 and P99 breakdown showed Bazel build and test, CTC and the resolve-dependencies check as the main consumers. The dependency check depends on neither CTC nor the build, so Uber moved it to run in parallel.
- Use it when
- A long job runs heavy steps one after another, and at least two of them read none of each other's outputs.
- Before applying it
- Separate jobs repeat checkout and setup. Measure the longest path, and keep the required aggregate waiting on every split check.
- GitHub Actions adaptation
- Compare the source system with your own workflow before applying this translation.
Company Playbooks
- Read Anthropic’s playbookRun only affected tests
- Read DoorDash’s playbookRun build and test in parallel, sharded across CI agents
- Read Linear’s playbookBenchmark the same job on faster hardware
- Read Mastra’s playbookStarSling runners for the slowest test workflow
- Read OpenAI’s playbookRank CI fixes by the minutes they save
- Read Ramp’s playbookMove a CPU-bound test suite to a faster CPU
- Read Shopify’s playbookLet an agent iterate against one CI metric
- Read Spotify’s playbookBuild arm64 on native arm64 runners
- Read Stripe’s playbookRun only the tests a change can reach
- Read Uber’s playbookBuild the merge-queue speculations most likely to be needed