We raised $3M for agent-native runners

Cut repeated setup and teardown from your test suite

Do expensive fixture work once per suite or per schema, and reset only the state a test reads, so each test spends its time on what it verifies.

How StarSling works

AI agents open the PR

StarSling agents inspect your workflow, apply this optimization, and open a reviewable PR automatically.

Do this

Set up shared resources once per suite or schema, reset test data in process, and keep cleanup lists limited to pending work. Use fast test-only defaults for intentionally expensive production operations, and keep real-path tests for the behavior each optimization touches.123456

Profile CPU-heavy setup in Vitest
- name: Profile CPU-heavy setup in Vitest
  run: >-
    pnpm exec vitest run --no-file-parallelism
    --execArgv=--cpu-prof
    --execArgv=--cpu-prof-dir=test-runner-profile
- name: Save CPU profiles
  uses: actions/upload-artifact@v7
  with:
    name: test-runner-profile
    path: test-runner-profile/*.cpuprofile

Avoid this

Repeated schema resets, package resolution, cleanup and hashing add up across every test group.

A test step with no timing baseline
- name: Run tests
  run: pnpm test:run
  # Changing fixture code without timing the full suite hides the effect.

Shipped by StarSling

Four merged Better Auth changes remove repeated work inside the test suite. Each card names its measured scope: test execution step, Vitest duration, or test job duration.

Reset data when the schema is already in place

Better Auth #9882 records the last schema pushed per database dialect. When it matches, the existing Prisma client clears table data in process: PostgreSQL uses TRUNCATE ... RESTART IDENTITY CASCADE, and MySQL uses DELETE in one interactive transaction so session settings stay on the same connection. A changed schema or an unsuccessful reset takes the full prisma db push --force-reset path. SQLite keeps its original push path. Preserve test isolation, and keep coverage of the real schema-push path.

Prompt for your coding agent
Fetch OPT85 "Tests Run in Many Processes Instead of One" from https://raw.githubusercontent.com/starslingdev/skills/main/skills/ci-speedup/references/optimization-patterns.md and check this repo against it yourself. Find the slowest test job gating the merge and read its step timings over the last 20 runs of its workflow with the GitHub CLI (gh run list, then gh run view --json jobs for each run). It applies when that suite's time goes to starting processes: tests that launch a subprocess, interpreter or CLI for each case, or a Rust crate with many integration-test binaries under tests/, each linked and started on its own. Measure it before changing anything: time one case through a subprocess against the same check called in-process, or count the test binaries and time the suite. If it applies, follow the entry's recipe: call the code in-process wherever the test does not exist to verify process behavior, or merge the integration tests into a single test binary, keeping every test case and assertion. Keep at least one test that runs the real process end to end. Report the suite's runtime before and after on the same machine. If nothing qualifies, tell me in one line why. Show me the diff and open a PR rather than applying it blindly.

Resolve a CLI once before repeated launches

Better Auth #10850 resolves the drizzle-kit CLI once and starts it with process.execPath. The adapter suite still runs all 407 schema pushes, with the same command options and database effects. Each launch becomes cheaper because it starts the resolved executable directly. Measure package resolution separately from the schema work to choose this fix.

Keep only pending rows in cleanup

Better Auth #10762 makes cleanup forget processed rows. Previously, each teardown revisited the rows created by earlier tests; 101,366 of 103,554 deletes targeted rows already gone. Retain rows whose cleanup throws or explicitly requests a retry, following the helper’s result contract. Check successive cleanup passes as well as the final database state.

Use fast defaults in shared test fixtures

Better Auth #10879 gives the shared fixture a fast salted SHA-256 hash and verify pair. Caller-supplied implementations and normalization stay intact, and production keeps its existing hasher. Keep the production hasher explicitly enabled in tests that verify stored-hash formats, upgrades, compatibility or security behavior. Use the faster default for tests whose subject is the surrounding application behavior.

Why it matters

A small setup cost becomes a large part of runtime when hundreds of test groups repeat it. Profile the suite before adding runners: reducing repeated work helps every run, including local development. Use the slow-test diagnosis to locate the cost, and see the Better Auth case study for the wider CI work.

When to use

Use it when

Use this approach when profiling shows shared fixtures, schema resets, CLI startup or teardown consuming a meaningful share of test time. Start with the slowest test job gating the merge.

Be careful when

Keep per-test setup when a test verifies initialization, migration, process behavior or isolation itself. Keep production cryptography in security and compatibility tests, and use separate database state for concurrent workers.

Verify on your repo

Hand this prompt to your coding agent (Claude Code, Cursor, and the like) to audit and fix this practice in your own repo.

Prompt for your coding agent
Diagnose why the test job in this repo's GitHub Actions workflow runs much slower than the same suite does locally. Do not change anything yet. First, open the workflow YAML under .github/workflows/ and identify the test job: check whether it uses a strategy.matrix with a --shard flag (Playwright, vitest), and whether it's the single longest job in the workflow's run history. Second, check whether the test job's install step has a matching caching action (actions/cache, or setup-node/setup-python/setup-go with cache: true) for its package manager. Third, profile shared fixture setup and search test helpers for production-strength password hashing or other deliberately expensive cryptography; if it dominates, propose a fast test-only default while preserving normalization and explicit overrides, and never change the production hasher. Keep the production implementation explicitly enabled in tests that verify stored-hash formats, upgrades, compatibility, or security behavior. Fourth, inspect teardown bookkeeping for cleanup passes that revisit successfully deleted IDs; propose removing IDs only after confirmed successful cleanup and retaining every unsuccessful or deferred cleanup according to the helper's result contract. Better Auth #10762 retains thrown failures and explicit `retry` results. Fifth, if this is a monorepo (turbo.json or nx.json present), check whether the test command actually scopes to the diff against the merge base rather than running everything. Sixth, grep the test source (packages/, e2e/, tests/) for fixed-duration waits like waitForTimeout, cy.wait(1000+), or raw sleep() calls, and estimate their total contribution to run time. Report back which causes you found evidence for, ranked by likely impact, before proposing any fix.

Prefer to check by hand?

  1. For the Vitest example, download the CPU profiles and open them in Chrome DevTools or VS Code to locate fixture and hook CPU work such as hashing. Add wall-clock timers around database calls and subprocess launches to measure their elapsed time. Keep profiling and concurrency settings identical in before-and-after runs.

  2. Record setup, test-body and teardown timings before editing the shared helper. Count subprocess launches and cleanup operations across successive test groups.

  3. Run both versions on the same runner type with the same test command, inputs and concurrency. Repeat the measurements and compare the test execution step.

  4. Confirm the same test files, cases and assertions run before and after. Check that schema changes, concurrent workers, cleanup retries and caller overrides preserve their behavior.

  5. Keep end-to-end coverage of the real CLI and production hasher where those behaviors are under test.

Go further

One fix, all of them, or forever.

You have the prompt for this one practice. Here is how much further you can take it, each step doing more for you than the last.

  1. Fix this one thing

    Copy the prompt above

    Hand OPT85 to your coding agent and fix it in your repo today.

    OPT85 covers the process-startup work in the schema-reset example: Better Auth #9882 replaces repeated CLI launches with an in-process reset.

  2. Fix everything, once

    Install the ci-speedup skill

    One prompt audits your whole repo against all 91 ci-speedup patterns (this one plus 90 more) and hands your agent every fix at once. Open source, MIT, runs locally.

  3. Keep it fixed, forever

    Install the StarSling GitHub App

    Connect GitHub and the fixes stay applied as your CI evolves, with agents that keep inspecting your workflows and opening optimization PRs you review.

More best practices for GitHub Actions

Where to go next in the CI best-practices catalog.

All GitHub Actions best practices

More ways to improve GitHub Actions

If you do not know why a run is slow yet, start with the diagnostic guide. Let an agent find which of these applies: /ci-speedup. If your workflows are already optimized but still slow, see our guide to fast GitHub Actions and GitHub Actions runner alternatives. Building containers in CI? The Docker workflow guide covers layer caching end to end. Want to see how your configuration measures up before changing anything? CI Score grades workflow config against a pass/fail rubric of these practices. It is not a speed measurement. To find exploitable workflow paths before attackers do, ci-secure checks the ten critical GitHub Actions attack vectors and lets you choose which fixes to apply.

FAQ

How do I find repeated test setup?

Profile shared setup and teardown hooks in the slowest suite. Count schema pushes, CLI launches, cleanup queries and password hashes, then compare their total time with test-body execution.

Can suite-level setup preserve test isolation?

Yes, when each case starts with the state it expects. Share stable schema and process initialization, reset mutable data between cases, and isolate concurrent workers. Exercise schema changes and reset recovery in dedicated tests.

Should I shard the suite first?

Measure the bottleneck first. Removing repeated fixture work reduces total execution; sharding distributes the remaining work. Apply both when each has a measurable benefit.

Sources

1Vitest test performance profiling (opens in new tab)

2Prisma db push (opens in new tab)

3Better Auth #9882 (opens in new tab)

4Better Auth #10850 (opens in new tab)

5Better Auth #10762 (opens in new tab)

6Better Auth #10879 (opens in new tab)

Last updated 2026-10-27