Earlier this year, Linear's CTO Tuomas assigned an issue titled 'CI costs are high' to a reporter, requesting improvements to make CI faster.
Earlier this year, Linear's CTO Tuomas assigned an issue titled 'CI costs are high' to a reporter, requesting improvements to make CI faster. Agents have significantly sped up code shipping, but validation processes have not kept pace, creating a bottleneck as development velocity increases. Every pull request must still pass through CI, leading to longer wait times for developers and agents, higher infrastructure costs, and delayed feedback loops.
To address this, Linear optimized CI performance by focusing on pull request wait times and runner resource consumption. Despite test suite size nearly quadrupling since January, wait times dropped from over 6 minutes to just above 5 minutes, while runner time per test was reduced by approximately 50%. These metrics reflect performance data from the first week of January.
Key improvements included upgrading infrastructure and tooling, optimizing gating jobs, reducing redundant setup, and enhancing test execution efficiency. The company's codebase is primarily TypeScript, though many optimizations are applicable across programming languages and toolchains.
Upgrading infrastructure involved migrating workloads from GitHub Actions to third-party runners featuring faster CPUs, higher-performance storage, and improved caching capabilities. In direct comparisons, jobs completed 34% faster on average, with specific workloads like type checking seeing a 52% reduction in duration.
A separate initiative modernized the toolchain by adopting tsgo, a native TypeScript compiler, which reduced the weekly median for type checking by 73%. This shift moved the bottleneck away from type checking entirely. Additionally, linting processes were streamlined by rewriting custom rules to operate on abstract syntax trees without relying on TypeScript type information. This change reduced API lint time by 68% and full-repository lint time by 55%, while also lowering memory consumption. The syntax-based approach also facilitated a later transition to Oxlint, as rules no longer depended on type analysis.
Efforts to optimize gating jobs focused on minimizing critical path dependencies. Early workflows began with change-detection jobs that determined which tests to run, but these required full repository checkouts even when only partial data was needed. By limiting fetch depth and eliminating unnecessary checkouts, the slowest change-detection job duration decreased from 94 seconds to 20 seconds, with some cases dropping to 7 seconds. For events like commit pushes and merge-queue entries, sparse, blobless checkouts with limited history sufficed, saving an additional 11 seconds.
Resilience improvements addressed intermittent checkout hangs on third-party runners. A custom composite action was implemented with retry logic, timeout thresholds, and cache utilization to prevent workflow stalls. This reduced the median duration of change-detection jobs from 26 seconds to 8 seconds, with p90 improving from 31 to 12 seconds and the slowest runs dropping from 138 to 37 seconds.
Repeated setup costs were mitigated through several strategies. Shared dependencies were preinstalled in base CI images, eliminating redundant installation steps across test shards. For example, Postgres client installation time per shard decreased from 7-8 seconds to near-zero. Similarly, pnpm workspace installations were restricted to only required packages, cutting install times from 44-73 seconds to 16-18 seconds per job.
Caching strategies were re-evaluated, with testing revealing that rebuilding dependencies was often faster than restoring caches. Cache restoration, which depended on frequently changing lockfiles, could take up to 28 seconds even on cache hits, providing no meaningful benefit while introducing variability. As a result, caching was abandoned in favor of targeted installs.
Further optimizations included avoiding unnecessary setup replay. Database migration histories were no longer re-executed on every run when schemas were unchanged; instead, generated snapshots were used, reducing database setup time from 12 seconds to 1-2 seconds per container.
Batching previously isolated short checks reduced setup overhead. Seven independent checks were consolidated into two jobs that executed multiple tasks concurrently, saving approximately 87,000 runner-minutes monthly—equivalent to 11.8% of total CI usage.
Test execution efficiency was enhanced by increasing parallelization. The test suite was expanded from three to eight shards, improving critical job speed by 19% and reducing costs. Vitest, the test runner, was reconfigured to allow shared module registries in non-isolated test files, enabling more efficient resource use. This change reduced the slowest shard duration from 300-379 seconds to approximately 195 seconds and cut total API shard runner time from 32.8 to 22 minutes per run. However, this approach carried correctness risks and required explicit opt-in per file, with certain test patterns left isolated for safety.
Sharding effectiveness depends on low setup overhead. Earlier optimizations reduced setup time from 8.3 minutes (with four shards) to 7.5 minutes (with eight shards), making expanded sharding practical. Without these improvements, the current test suite would take roughly 11 minutes, doubling current wait times. Given ongoing growth—approximately 2,000 new tests added weekly—continuous CI optimization remains essential. The lessons learned from this process are being applied to emerging bottlenecks as the system evolves.
- Publisher
- Hacker News
- Reliability
- high
- Published
- 9/22/2026, 12:25:23 PM
- Retrieved
- 9/22/2026, 12:25:23 PM
- Relevance
- 80%
- Confidence
- 85%

