20 September 2026
The build is broken. Not conceptually, but practically. You push a commit, and then you wait. You wait for the CI runner to spin up, for dependencies to resolve, for the test suite to grind through thousands of cases, for the container image to build, for the deployment to propagate. By the time you get feedback, you have already moved on to something else, and the context switch costs you more than the wait itself.
This is the feedback loop problem, and it is the single most expensive inefficiency in modern software engineering. Not because any individual wait is catastrophic, but because the aggregate effect compounds across teams, across time zones, and across the thousands of micro-decisions developers make every day. A slow feedback loop does not just waste time. It changes behavior. It makes developers batch changes, avoid running tests locally, and merge code they are not confident about.
The tools and practices that defined CI/CD in the 2010s and early 2020s are reaching their limits. The next phase requires a fundamental rethinking of how we architect the path from code commit to production confidence.

The problem is not the monorepo itself. It is the assumption that every change requires the full validation surface. Most changes do not.
There is a psychological dimension here too. When tests take too long, developers stop running them locally. They push and hope. The CI system becomes the first line of defense rather than the last, and the feedback loop stretches from seconds to minutes to hours.
The container tax is not just time. It is complexity. Debugging a failed build inside a container is harder than debugging a failed build on a local machine. The abstraction that makes deployment consistent makes development slower.
This sounds obvious, but it is surprisingly hard to implement correctly. You need a dependency graph that maps source files to build targets and test suites. You need to handle transitive dependencies. You need to account for configuration changes that affect everything. And you need to do all of this without introducing false negatives, where a test that should have run does not.
Tools like Bazel, Nx, and Turborepo have made significant progress here, but the principle extends beyond build systems. The same logic applies to linting, type checking, security scanning, and deployment. If a change does not affect a service, that service should not be redeployed.
This requires three things: fast local builds, representative test environments, and tooling that makes it easy to run targeted subsets of the test suite. The goal is not to replace CI but to shift the majority of validation to the left, so that CI becomes a confirmation step rather than a discovery step.
The trade-off is real. Local environments drift from CI environments. Developers have different hardware. Some tests genuinely require infrastructure that cannot run locally. The answer is not to abandon local validation but to be intentional about which checks belong where.
The challenge is correctness. Incremental systems must be able to detect when their assumptions are invalidated. A cached test result is only valid if the code under test, its dependencies, and the test environment are all unchanged. Getting this wrong leads to false confidence, which is worse than slow feedback.
Modern build systems handle this well for compilation. Test caching is harder because tests often have side effects, depend on external state, or are non-deterministic. The solution is not to cache everything but to be selective about what can be safely cached and to have mechanisms for invalidating caches when necessary.
Effective parallelism requires isolation. Each test should run in its own environment, with its own resources, and produce its own logs. This is where containers and ephemeral environments shine, but only if the overhead of creating them is low enough to justify the parallelism.

The benefit is obvious: less redundant work. The cost is infrastructure complexity. You need a cache server, a strategy for cache invalidation, and a way to handle cache misses gracefully. You also need to think about security. Caching build artifacts across teams means that a compromised artifact could propagate.
Tools like Bazel's remote cache, Turborepo's remote caching, and GitHub Actions cache provide implementations of this pattern. The key is to start with a narrow scope, such as caching dependencies and compiled libraries, and expand from there.
This is powerful but risky. If the dependency graph is incomplete or outdated, you will miss tests that should have run. The mitigation is to run the full suite periodically, such as on merge to main or on a nightly schedule, and to treat the impact analysis as an optimization for pull requests rather than a replacement for comprehensive testing.
The trade-off is between speed and coverage. Test impact analysis speeds up the feedback loop but introduces the risk of false negatives. Teams need to decide how much risk they are willing to accept in exchange for faster feedback.
The trade-off is cost. Warm pools consume resources even when idle. The optimal strategy depends on the ratio of environment creation time to environment usage time. If environments take 5 minutes to create and are used for 30 minutes, a warm pool may not be worth it. If they take 5 minutes to create and are used for 2 minutes, a warm pool is essential.
This is a simple optimization, but it requires discipline. Teams must resist the temptation to run everything in parallel, which wastes resources on tests that will fail anyway. They must also ensure that the fast tests provide meaningful signal, so that failures are caught early rather than deferred to the slower tiers.
The trade-off is migration cost. Moving from one build system to another is disruptive and risky. The benefit is long-term: a unified feedback loop that is easier to optimize and reason about.
Optimization should target the critical path, not the individual components. Sometimes the biggest win is not making a test faster but making it report its results sooner.
Flakiness must be treated as a first-class problem. Tests that are flaky should be quarantined, investigated, and fixed or removed. The cost of a flaky test is not just the time it wastes but the trust it erodes.
The optimal level of parallelism depends on the workload. Teams should measure and adjust rather than assuming that more parallelism is always better.
CI should be treated as a product, with its own metrics, its own users, and its own roadmap. Teams should measure pipeline duration, failure rates, and time to feedback. They should invest in improving the pipeline as deliberately as they invest in improving the application.
It is fast. The majority of changes receive feedback in under five minutes. This is not a hard requirement, but it is a useful target. Changes that require more extensive validation are the exception, not the rule.
It is reliable. Flaky tests are rare and are treated as bugs. Developers trust the results and do not feel the need to rerun pipelines out of skepticism.
It is transparent. Developers can see what is running, why it is running, and how long it will take. Failures are reported with clear, actionable messages.
It is incremental. Most changes only trigger the work that is necessary. The full suite runs periodically, but it is not the default.
It is local-first. Developers can run the same checks locally that CI runs, and they do so regularly. CI is a confirmation step, not a discovery step.
Teams that own their own pipelines tend to have faster feedback loops than teams that rely on a centralized platform team. This is because they can optimize for their specific needs and iterate quickly. Centralized platforms provide consistency and economies of scale, but they can become bottlenecks when they are not responsive to the needs of individual teams.
The right balance depends on the organization. Small teams should own their pipelines end-to-end. Large organizations should provide shared infrastructure and best practices but allow teams to customize where it matters.
The tools and practices described here are not exotic. They are available today. The challenge is not knowing what to do but doing it consistently and measuring the results.
Start with the critical path. Measure the time from commit to actionable feedback. Identify the bottlenecks and address them one at a time. Invest in local-first validation, change-aware execution, and incremental everything. Treat flakiness as a bug, not an inconvenience.
The feedback loop is the heartbeat of software development. Make it fast, make it reliable, and make it something developers trust.
all images in this post were generated using AI tools
Category:
Developer ToolsAuthor:
John Peterson