We raised $3M for agent-native runners

Compute waste

Find CI work that consumes compute without helping the next result. Compare ways to bound, reuse, or remove that work.

Where to start

Diagnose first: are you paying for redundant jobs, excessive setup, idle wait, or a costly runner class?

Compute cost depends on how much work runs, how long it runs, and the resources it occupies. Start with repeated or obsolete jobs, expensive setup, and workloads that exceed their useful timeout. Compare cost and time together after each change.

12 techniques with source-backed company examples.

CI technique

Cancel superseded pull request runs

Free runner capacity by canceling validation that a newer commit has replaced. Keep required main-branch and release runs intact.

How it works

Group validation runs by the pull request or branch they validate. When a newer commit starts another run in that group, cancel the older validation. Scope the cancellation rule to work that the new run replaces; deployment, release and migration workflows need their own completion rules.

Cancel superseded runs, never on main

Anthropic's Python and TypeScript SDKs added a workflow concurrency group keyed on the branch, so a newer push cancels the older run. On main the group is keyed on the commit, so main runs never cancel each other.

Use it when
Developers or agents push to the same branch several times while CI is still running.
Before applying it
Deploy, publish and migration workflows keep running to completion; only runs whose result a newer commit replaces are cancelled.

Cancel obsolete runs using the correct event identity

Mastra added workflow concurrency and cancellation to lint, docs and workspace workflows. Related changes handled workflow_run identity and separated comment-driven approval updates from PR updates.

Use it when
Repeated pushes leave obsolete verification jobs occupying runners.
Before applying it
Key the group on workflow plus the logical PR or ref, with safe fallbacks, so a new commit cancels the old run. Keep release and destructive side-effect jobs out of indiscriminate cancellation.

Cancel superseded pull request runs

Stripe iOS added a concurrency group to its dead-code workflow, keyed by pull request number, with cancel-in-progress so a new push stops the previous scan.

Use it when
Superseded pull request runs keep consuming runners.
Before applying it
Scope cancellation to replaceable pull request validation. Releases, deployments, migrations and default-branch runs that must finish keep cancel-in-progress off.

Cancel superseded pull request runs

Base Web added a concurrency group keyed on the workflow and pull request number to its PR Check workflow, with cancel-in-progress, so a new push stops the run it supersedes. NilAway did the same for its Golden Test workflow.

Use it when
Developers push several commits to a pull request in quick succession and each push starts a full run.
Before applying it
Key the group on the pull request so runs for different pull requests never cancel each other, and keep default-branch and release runs out of cancel-in-progress.
CI technique

Put time limits on jobs that can hang

Set job timeouts from normal run history so stalled checks release runners and produce a failure that can be investigated.

Bound flaky job runtime

Anthropic describes running tests that were already flaky or failing across the board. As a StarSling pattern for GitHub Actions, a timeout set from successful run history stops a hanging job from holding a runner for six hours.

Use it when
A non-matrix integration job has timed-out runs near the 360-minute default and at least three successful runs to measure.
Before applying it
A timed-out required check still blocks the merge, so the hanging tests get fixed by name.

Bound integration jobs that can hang

Mastra added explicit timeouts to integration and change-detection jobs, then broadened timeout coverage. Later checkout timeouts were lengthened, showing why the bound needs observed tail data.

The combined store workflow keeps its job timeouts in test-combined-stores.yml. Source

Use it when
A database, checkout or external service occasionally hangs beyond useful execution time.
Before applying it
A timeout caps the runner minutes a hung job can burn. Leave room for normal tails and diagnostics.

Bound a test job that can hang

After tests looped indefinitely during development, Job Iteration added a five-minute timeout to a build matrix that normally runs in under a minute. A hung leg now stops at five minutes; the platform default is 360.

Merged 2026-06-04; the timeout is on the default branch. Source

Use it when
A job with no timeout-minutes can stall and hold a runner until the platform default.
Before applying it
Set the ceiling from the job's real duration with headroom for cold setup.

Cap hung test jobs with timeouts

The confidence-sdk-flutter iOS test job could hang on the simulator and run for six hours. The change adds a 45-minute job timeout and a 30-minute timeout on the integration test step, boots the selected simulator before the tests and removes the blocking simctl bootstatus wait.

Use it when
A job sometimes hangs and runs until the platform's default limit, six hours on GitHub-hosted runners.
Before applying it
Set each timeout well above the longest healthy run so slow runs still finish; the passing iOS run on this change took 6m34s against a 45-minute cap.
CI technique

Remove repeated build steps

Trace artifact producers and consumers so package, release, or test stages reuse work instead of triggering the same build again.

Build release artifacts after the final version change

Stable publishing removed an early build and kept one build after prerelease exit and installation. Earlier version-PR automation removed builds from the metadata-only versioning step.

Use it when
A release job builds twice around version or manifest changes.
Before applying it
Keep the build that produces published artifacts after all version changes.

Let the package lifecycle build once

Codex Security removed the explicit build steps before pnpm pack, because the package's prepack script already runs the build.

One build per packaging job, run by prepack Source

Use it when
A package lifecycle hook repeats a build in the same job.
Before applying it
Remove the explicit build only when the exact packaging command runs the hook. Disabled hooks or a changed lifecycle would ship a stale package.

Let test and lint stages reuse the build stage

Confidence's Dockerfile ran each component's test and lint stages as siblings from the same base, so each stage compiled the source on its own. The change adds a build stage per component and derives the test and lint stages from it, letting cargo, Go and Maven reuse the compiled artifacts across six components.

In a local no-cache Docker build on an Apple M1 Max, total compute across the affected stages fell 61%, from 1807s to 712s. Source

Use it when
A multi-stage Dockerfile or build compiles the same source separately for its build, test and lint stages.
Before applying it
Test and lint now wait for the build stage. Components that need a different build profile keep their own base.

Stop rebuilding just to export artifacts

After the cached Docker build and test step, every Confidence CI run ran a second docker build to export the WebAssembly artifact to the runner, then wrote a step summary listing the artifacts. The change removes both steps from the job.

Use it when
A job runs a second build only to copy outputs out or print a summary that nothing downstream reads.
Before applying it
Check that no later step, release or reviewer uses the exported files before removing the export.
CI technique

Run CI once per pull request commit

Remove overlapping push and pull request triggers so each change receives one intended validation run.

Run CI once per change

Anthropic reports more CI jobs in a given day. As a StarSling pattern for GitHub Actions, removing duplicate push and pull_request runs and metadata-only triggers keeps each change to one run.

Use it when
Run history shows duplicate events for the same commit, or metadata events rerun checks that do not depend on that metadata.
Before applying it
Preview deployments, cache warming, release side effects and metadata-dependent checks keep the triggers they need.

Run each pull-request commit once

The Ruby extension's workflow ran on every push and every pull request, so each in-repo branch got two identical runs; for one commit they started three seconds apart. Shopify/vscode-shopify-ruby#808 limits push to main and adds a concurrency group whose cancellation applies only to pull_request events, so a main run that is already running is never cancelled, though a main run still waiting in the same group is replaced by a newer push.

Merged 2026-09-18; the workflow on the default branch is unchanged since. Source

Use it when
The same commit runs equivalent validation through both push and pull_request, and developers push several times in a row.
Before applying it
Check that the two runs test the same ref with the same permissions before treating them as duplicates.

Run push builds only on the default branch

target-determinator's presubmit workflow ran on every push and on every pull request, so a commit pushed to a pull request branch in the repository ran the test matrix twice. The change restricts the push trigger to main; pull requests still run through the pull_request event.

Use it when
A workflow triggers on both push and pull_request, and the push trigger covers every branch.
Before applying it
Branches without a pull request no longer get push builds; the workflow keeps workflow_dispatch for running them by hand.
CI technique

Bound agent execution and retries

Set explicit time, turn, or retry budgets so an agent that cannot finish returns control rather than consuming unbounded CI capacity.

Give every agent in a CI gate a soft and a hard timeout

One early problem in DoorDash's review agent: the same model request could repeat over and over without advancing its turn counter, so max-turn limits never fired, and a single overall timeout still allowed one stuck agent to burn 20 minutes of tokens. DoorDash moved to tighter per-agent timeouts. The soft timeout interrupts the agent and asks it to drop anything speculative and return only verified findings; the hard timeout is the final kill switch. DoorDash's feature-flag cleanup agents each run under a one-hour hard timeout.

Use it when
An agent or long-running step inside CI can loop without progress, and only a job-wide timeout stops it.
Before applying it
Set the soft limit from measured run times so normal runs finish untouched, and report a soft-stopped run as partial.

Cap an agent's turns and session retries

Translated to GitHub Actions

What Spotify built
Spotify's Fleet Management loop caps a session at 10 turns and three session retries.
Your GitHub Actions version
Cap an agent's turns per session and its session retries.

Spotify's first agentic loop for Fleet Management gave the agent a prompt and the files in scope. The agent edited files over a few turns, took in feedback from the build system on each turn, and the task ended once the tests passed or the limits were exceeded: 10 turns per session and three session retries in total. Spotify's prompt guidance for migrations asks for one change at a time, so a session stays within its context window.

Use it when
An agent or bot job can iterate on its own, and a job-wide timeout is the only limit on a run.
Before applying it
Set the limits from your longest successful sessions. A task that needs more turns is a task to split into smaller changes.
GitHub Actions adaptation
Compare the source system with your own workflow before applying this translation.
CI technique

Run shared validation once

Remove overlapping task or matrix invocations that perform the same check on the same inputs.

Avoid repeating CI checks in a version bot’s commit hook

The versioning action disabled Husky because separate workflows already handled its precommit checks. This adapts the duplicate-command recipe and depends on required CI running the same checks.

Use it when
An automated version PR repeats hooks already enforced by required CI.
Before applying it
Never bypass a check merely because it is expensive. Prove equivalent file scope and failure propagation in required workflows; retain checks that only the hook performs.

Run snapshot checks once in a dedicated lane

Stripe iOS re-records snapshots in a dedicated job and skips those snapshot classes in the main unit-test jobs only when that dedicated job is part of the pipeline. Standalone framework jobs keep every test.

Use it when
Snapshot or generated-output checks run both on their own and inside a broad suite.
Before applying it
Skip a class in one lane only while a required lane runs it, and keep runtime variants and aggregate results unchanged.

Stop invoking tests twice through overlapping tasks

Stripe Android’s screenshot verification already ran the unit tests. A new dispatcher task runs screenshot verification for modules that have it and unit tests for the rest, so each module runs its tests once. Stripe .NET removed a Debug build and test pass that repeated the Release pass for an SDK with no configuration-specific code.

Use it when
A test wrapper invokes another test task that CI also calls directly, or two configurations run the same code.
Before applying it
Compare the test and screenshot inventories before and after, and keep a configuration that has its own code paths or its own published build.
CI technique

Share setup across small checks

Combine compatible short checks so checkout and dependency setup do not dominate the time spent doing useful work.

Pay setup twice instead of seven times

Linear consolidated seven small independent checks into two jobs and ran their tasks concurrently. Based on June usage, the change saved roughly 87,000 runner-minutes per month, equivalent to 11.8% of Linear’s total CI usage.

Use it when
Independent short jobs repeat more checkout and dependency setup than useful work.
Before applying it
Keep every check and its exit status, and keep the remaining critical path as short as before.

Share setup across compatible small checks

Stripe iOS combined several pairs of small jobs, including snapshot recording with the localization upload and the Connect and Crypto UI tests, so each pair pays for setup once.

Use it when
Independent small jobs spend more time on repeated setup than on their own work.
Before applying it
A combined job runs its parts in sequence, so compare the new critical path with the setup saved. Keep required-check names through an aggregator, and split again when a combined job becomes the long pole.
CI technique

Wait until a draft is ready for expensive checks

Avoid spending runner time on expensive validation until a draft pull request becomes ready for review.

Run expensive checks when a draft becomes ready

Checkout Kit skips its change-detection job, and so every platform job behind it, while a pull request is a draft. Adding ready_for_review to the trigger types starts a full run as soon as the draft is marked ready.

Merged 2026-06-16; both lines are on the default branch. Source

Use it when
Developers push to draft pull requests many times before asking for review.
Before applying it
Without ready_for_review, a draft promoted to ready keeps its skipped result on an untested commit.

Wait until a draft is ready for expensive checks

SubmitQueue added draft guards to every CI job and its required aggregate, then added ready_for_review to the workflow events. Pushes to main and merge-queue runs keep their validation.

Drafts skip the full suite, and it runs when the pull request is marked ready for review. Source

Use it when
Drafts receive frequent exploratory commits and your team agrees that expensive checks can wait.
Before applying it
Drafts get feedback later, so agree on it as a team policy. Test draft, ready, reopened, main and merge-queue events so every required check still reports.
CI technique

Package once, and build full installers after merge

Drop the build step that packaging repeats, and build the unpacked app pull requests test.

Package once, and build full installers after merge

The desktop job ran a Build step and then packaging, which runs the same electron-vite build again. The PR removed the standalone Build step. Pull requests now package the unpacked app that the end-to-end journeys consume; pushes to main still build and inspect the full installers.

Use it when
A job builds explicitly before a packaging or test command that builds again, or pull requests build release artifacts that no test reads.
Before applying it
Read the wrapper scripts before removing a build step. The full installers still need a post-merge run that someone watches.
CI technique

Reduce the setup floor before increasing shard count

Each extra shard repeats checkout, dependencies and container setup.

Reduce the setup floor before increasing shard count

Each extra shard repeats checkout, dependencies and container setup. Before its setup work, Linear’s test job used 4 shards and spent 8.3 minutes on setup; afterward it ran 8 shards with 7.5 minutes of setup.

Use it when
More shards would repeat substantial fixed setup work.
Before applying it
Remeasure per-shard setup before changing shard count.
CI technique

Remove caches that cost more than they save

Measure cache restore and upload time against the work avoided. Remove caches whose transfer cost outweighs their benefit.

Remove a cache when restoring it costs more than rebuilding

Linear measured a node_modules cache hit at about 28 seconds to restore, against roughly 7.5 seconds for a filtered install. The cache key depended on a frequently changing lockfile, so Linear removed the cache.

Use it when
Comparable measurements show restore and save overhead outweighing the work avoided.
Before applying it
First try a package-manager store or a narrower cache, and separate miss-rate problems from an expensive successful restore.
CI technique

Stop writing caches that no later run restores

Remove cache writes and archives that have no consumer, eliminating work without changing the build result.

Turn off a build cache that CI never restores

Next.js caches Webpack, ESLint and SWC under .next/cache by default, and for large projects the Webpack cache can exceed 5 to 10 GB. CI jobs never saved that cache between runs, so every write was wasted disk activity. Disabling the Webpack disk cache in CI saved three minutes per run.

Use it when
A build tool writes a large local cache on every CI run, and no cache step saves or restores that directory.
Before applying it
Keep the cache wherever a later run restores it (developer machines, or CI with a cache step whose hit rate you have measured).

Company Playbooks