I'm on a three-person team at a very early-stage company, and each of us is shipping roughly 40–50 pull requests a day while we prioritize getting a functional product out quickly. As our throughput increased, the workflow started showing cracks. We originally required every pull request to be up to date with the main branch before merging, but changes landed so frequently that pull requests were constantly becoming stale and developers were blocked waiting for fresh CI runs. We've reduced the amount of pre-merge CI and moved more testing to after merge, which improved throughput but feels like we're trading away too much confidence. Has anyone dealt with a similar volume? What workflow, merge strategy, or CI optimizations worked better for you?
5 Answers
The main issue is probably the requirement that every pull request be tested against the newest version of main. Each merge invalidates everyone else’s work and causes the same checks to run again. Keep pre-merge checks small and deterministic—linting, focused unit tests, and essential validation—then let a merge queue or batched merge process run the final integration checks. If the queue becomes slow, separate required checks from the broader test suite and run the expensive tests asynchronously after merge with automatic alerts or rollback when main regresses.
At this volume, define a very clear confidence boundary. A small set of fast, fail-closed checks should block merges, while the full integration and regression suite can run after merge. Track pre-merge runtime, queue wait time, post-merge failures, and rollback frequency separately. Otherwise, reducing pre-merge checks may make the team look faster while simply moving failures downstream.
Consider moving closer to trunk-based development. Keep branches short-lived, avoid requiring repeated rebases just to prove freshness, and merge small changes frequently. For related work that must be integrated in order, stacked changes can help, but stacking alone won’t solve a required-check storm. If immediate merging is still too expensive, batch changes into internal releases every few hours instead of treating every individual pull request as a full release.
Batching can be a good fit for an early-stage system, but it should be temporary and measured. As the codebase and release process grow, the batch interval may need to increase or the process will become another queue.
Repository and task structure can reduce contention too. Keep each change focused on one feature or area, avoid having everyone edit the same files, and split shared documentation or configuration into smaller ownership boundaries where possible. That won’t fix slow CI by itself, but it reduces conflicts and the number of pull requests that need to be rebased or repaired before merging.
Before changing the workflow too much, optimize the CI pipeline itself. Use parallel or matrix jobs, cache dependencies, use more capable runners for compute-heavy tests, perform shallow checkouts, install only what each job needs, and use path-based filters so unrelated changes don’t trigger every job. Also cancel obsolete runs when a newer commit supersedes them. These improvements can remove a lot of wasted setup and duplicate work at high throughput.

A merge queue can definitely become its own bottleneck if the required job is too large. Measuring queue wait time separately from actual CI runtime helps identify whether the problem is runner capacity, setup time, or the test suite itself.