One Maintainer Cut a Single Monorepo Tool That Replaced Three Dedicated CI Systems

Jul 17, 2026 By Yusuke Tanaka

Three CI systems. Three configuration languages. Three sets of failure modes. And one maintainer who decided enough was enough.

It started as a frustration most engineers recognize: the web pipeline broke because a shared library updated its interface, but the mobile pipeline didn't catch it until deployment. The backend pipeline ran integration tests on a schedule, not on change, so a breaking database migration sat undetected for hours. Each system had its own YAML dialect, its own caching strategy, its own way of handling secrets. The team spent roughly one full day per week just triaging CI failures across the three silos.

The solution came from an unexpected direction. A single engineer, working evenings and weekends over several months, built a custom monorepo CI tool that collapsed all three pipelines into one coherent system. The result: pipeline runtime dropped from 45 minutes to under 12. Monthly CI costs fell by about 60%. On-call alerts related to build failures went from several per week to nearly zero.

This is the story of how one person did it, what tooling choices made it possible, and why most companies with multiple CI systems should consider following suit.

The Three-CI Trap Most Companies Don't See

When a company grows organically, each team often picks its own CI tool. The web team chooses GitHub Actions because it's familiar. Mobile adopts Bitrise or CircleCI for platform-specific emulators. Backend goes with Jenkins or GitLab CI for self-hosted flexibility. On paper, this seems like sensible autonomy. In practice, it creates a maintenance burden that grows superlinearly with headcount.

Each pipeline has its own failure patterns. Web builds fail because a Node module cache expires. Mobile builds fail because the iOS simulator version changed. Backend builds fail because a Docker layer cache got invalidated. The team responsible for keeping all three running — often a single DevOps or platform engineer — ends up context-switching between three different debugging workflows. One maintainer described it as "being on call for three separate fires, each with its own fire extinguisher that works differently."

The overhead isn't just operational. It's cognitive. Developers learn the idioms of one pipeline and then need to re-learn the same concepts in another language. A conditional step in GitHub Actions looks nothing like a conditional step in Jenkins. Cache keys, artifact paths, environment variables — every detail differs. New hires spend their first weeks just learning how to navigate the CI maze.

Worst of all, cross-service integration tests become nearly impossible. The web team's pipeline triggers on a PR, but the backend pipeline runs on a cron schedule. By the time both pass, the code has diverged. The company ends up with a separate integration test suite that runs nightly and breaks constantly, adding yet another maintenance surface.

What a Monorepo CI Actually Does Differently

A monorepo CI system doesn't just reuse the same YAML file across services. It fundamentally changes how builds are scheduled and executed. Instead of time-based triggers or manual webhooks, the system uses a dependency graph to determine exactly what needs to run for each change.

When a developer pushes a commit that only touches a shared utility library, the system rebuilds only the packages that depend on that library. It doesn't run the entire web test suite or redeploy the backend. This graph-aware scheduling is the core insight that makes monorepo CI efficient. Tools like Bazel, Nx, and Turborepo all implement this pattern, but the key is that the same graph applies across all services in the repository.

Cache layers become shared automatically. If the mobile and web services both depend on the same compiled TypeScript package, the build output is stored once and reused. There's no duplicate artifact storage across systems. No manually configured cache keys that expire at different times. The cache is simply a content-addressable store keyed by the hash of the build inputs.

Test selection also changes. Instead of running every test on every commit, the system queries the dependency graph to find only the tests affected by the change. If the change is in a deep leaf dependency, only that package's unit tests run. If it touches a cross-cutting interface, the system expands the test set to include all downstream consumers. This semantic test selection, similar to Bazel's query language, dramatically reduces pipeline runtime for most commits.

The Tooling Choice That Made It Possible

The maintainer in this story didn't start from scratch. They evaluated existing tools and found that no single one solved all three problems. Instead, they built a custom wrapper around three open-source components: Nix for reproducible environments, Turborepo for task orchestration, and Earthly as a bridge between local development and CI. The wrapper, written in Rust, tied them together with a single configuration file.

Nix provided the foundation. Build environments became fully reproducible — every dependency, every system tool, every environment variable was pinned to a cryptographic hash. This eliminated the "works on my machine" problem that plagued the three former pipelines. If a build succeeded on a developer's laptop, it would succeed in CI, because the environment was identical. The trade-off was a steeper learning curve for engineers unfamiliar with Nix's functional approach to package management.

Turborepo handled the task orchestration layer. It understood the dependency graph between packages and could parallelize builds across multiple cores and machines. Its remote caching feature meant that if a package had already been built with the same inputs, the result was fetched from a shared cache rather than rebuilt. The maintainer configured Turborepo to use the same cache for local development and CI, so engineers never waited for a build that had already been done.

Earthly served as the bridge. It provided a Dockerfile-like syntax that worked both locally and in CI, so developers could test their build steps without pushing a commit. The maintainer wrote Earthly targets for each service's build and test steps, then used Turborepo to orchestrate them in dependency order. The Rust wrapper parsed a single YAML file that defined the entire pipeline — no more jumping between three different config formats.

Rust was chosen for its startup speed and strict error handling. The wrapper needed to parse configuration, validate the dependency graph, and launch builds in under a second. Python or Node would have added hundreds of milliseconds of startup time. Rust's type system also caught configuration errors at compile time rather than at runtime, reducing the number of failed pipeline runs due to misconfiguration.

Real Numbers from a Production Migration

The migration took place over roughly three months at a mid-stage startup with about 40 engineers. The three former CI systems were GitHub Actions for web, Bitrise for mobile, and GitLab CI for backend. The monorepo already existed — all three teams had been committing to the same repository — but each used a separate pipeline configuration.

After the migration, pipeline runtime for the average commit dropped from 45 minutes to under 12 minutes. The 75th percentile commit, which touched multiple services, ran in about 18 minutes. The key driver was the dependency graph: roughly 70% of commits touched only a single service, and those commits no longer triggered builds for unrelated services.

Cache hit rates exceeded 80% after two weeks of tuning. The content-addressable cache meant that if two commits touched different files in the same package, only the changed files were rebuilt. Shared dependencies like TypeScript, React, and Kotlin standard libraries were cached once and reused across all services. The maintainer estimated that the cache saved about 30 minutes of build time per developer per day.

Monthly CI costs dropped by roughly 60%. The three former systems each had separate compute and storage bills. Consolidating onto a single infrastructure — a cluster of self-hosted runners managed by the Rust wrapper — eliminated the duplicated overhead. The company also stopped paying for premium features like parallel test splitting and cache management that were now handled by the monorepo tool.

On-call alerts related to CI failures fell from several per week to nearly zero. The maintainer attributed this to the deterministic nature of Nix builds: if a build failed, it was because of a code change, not an environment mismatch. Developers reported faster feedback loops in daily standups, with many noting that they no longer had to check three different CI dashboards to understand why their branch was red.

The Hidden Complexity Nobody Warns You About

Despite the success, the migration uncovered several pain points that the maintainer hadn't anticipated. Cross-service integration tests required careful scoping. In the old system, each team owned its own integration test suite and ran it on its own schedule. In the monorepo, the dependency graph naturally expanded test selection to include downstream consumers, but this sometimes triggered integration tests for unrelated services. The maintainer had to add explicit boundaries — service A's integration tests would only run if service A's code changed, not if a shared library changed.

Secrets management became more complex. The three former pipelines each had their own mechanism for storing API keys, database passwords, and deployment tokens. The monorepo tool needed a unified approach that worked across all services. The maintainer chose HashiCorp Vault, but configuring it to work with Nix's sandboxed builds required several weeks of iteration. Environment-specific secrets — staging vs. production — had to be injected at the right stage of the build without leaking across services.

Rollback strategy became harder. In the old system, rolling back a service meant reverting its own pipeline. In the monorepo, a single commit could touch multiple services. Rolling back required either reverting the entire commit or carefully cherry-picking changes. The team adopted a practice of tagging each commit with a metadata file indicating which services were affected, so rollbacks could be scoped to specific packages.

Developer onboarding required understanding the whole graph. New hires needed to learn not just their own service's build process but how it fit into the larger dependency chain. The maintainer created a visual map of the dependency graph and documented the most common failure modes. Even so, the learning curve was steeper than with the isolated pipelines. Some engineers initially resisted the change, preferring the simplicity of a single-service CI config.

Tooling updates broke all three former systems at once. When the maintainer updated the Rust wrapper or changed the shared configuration, any mistake affected every build across the entire organization. A misconfigured cache key could cause a full rebuild of every service, costing hours of compute time. The maintainer implemented a canary process: changes to the tooling were first tested on a small subset of builds before being rolled out globally.

When Not to Merge: Anti-Patterns in Monorepo CI

Not every organization should consolidate its CI systems. Teams with strong ownership boundaries per service often benefit from isolated pipelines. If each service has its own deployment cadence, its own security requirements, and its own team of engineers who rarely touch other services, the overhead of a unified CI may outweigh the benefits. The monorepo tool forces coordination that these teams may not want.

Organizations using multiple cloud providers for isolation may also find monorepo CI awkward. If the web service runs on AWS and the backend runs on GCP, a single pipeline must handle credentials for both. This increases the attack surface for secret leakage and makes it harder to enforce least-privilege access. In such cases, separate pipelines with explicit cross-service contracts can be safer.

Projects with third-party CI compliance requirements — such as SOC 2, HIPAA, or PCI DSS — may need audit trails that map to specific services. A single pipeline that handles multiple services can complicate audit logging. Each service's build steps must be clearly separated in the logs, and any shared infrastructure must be scoped appropriately. The maintainer spent several weeks ensuring that the unified pipeline met the company's existing compliance controls.

Very large monorepos — those exceeding 10 GB in source code — require additional tooling for sparse checkout and partial cloning. Without it, cloning the entire repository for every build becomes prohibitively slow. The maintainer's tool used Git's sparse checkout feature to fetch only the packages needed for a given build, but this added complexity that a smaller monorepo wouldn't face.

Finally, the bus factor risk is real. When a single maintainer owns the entire CI system, their departure can leave the organization stranded. The maintainer in this story documented the tool extensively and trained two backup engineers, but the knowledge gap remained significant. Companies considering this approach should invest in cross-training from the start.

How to Start the Merge Yourself

Before writing any code, audit your current CI usage across all three systems. Gather metrics on pipeline runtime, cost per build, cache hit rates, and failure frequency. Identify the most duplicated build logic — common dependencies, shared test runners, similar deployment scripts. These are the first candidates for consolidation.

Next, identify the most cacheable steps. In the maintainer's case, installing dependencies and compiling shared libraries accounted for about 60% of build time. These steps benefited most from a shared content-addressable cache. Prototype the cache layer first, using a tool like Turborepo or Nx, and measure the improvement before building the full pipeline.

Prototype with a single low-risk service. Choose a service that has few external dependencies and a stable test suite. Configure the monorepo tool to build and test that service, while leaving the other two pipelines running as before. Run both the old and new pipelines in parallel for at least two weeks. Compare not just runtime and cost, but also developer satisfaction — ask the team if they notice any difference in feedback loops or debugging effort.

Once the prototype is stable, gradually add the remaining services. The maintainer added mobile next, then backend last, because backend had the most complex integration tests. Each addition required updating the dependency graph and adjusting cache configurations. The parallel run period allowed the team to catch regressions before cutting over.

Finally, measure the results against your baseline. Pipeline runtime, cache hit rate, monthly cost, and on-call alert frequency are the key metrics. But don't neglect the qualitative side. Ask developers whether they find the unified pipeline easier or harder to debug. A faster pipeline that's harder to understand may not be a net win.

The maintainer's story isn't a universal prescription. It's a case study in what's possible when one person decides to simplify a system that has grown beyond its original design. The tools exist. The patterns are documented. The question is whether your organization is ready to consolidate its CI before the complexity becomes unmanageable.

Recommend Posts
Tech

One Inference Engineer's GPU Swarm Saved a Week per Pipeline Run

By Deepa Iyer/Jul 17, 2026

How a mid-size AI lab cut fine-tuning time from 7 days to 14 hours by swapping a homogeneous A100 cluster for a dynamic swarm of heterogeneous GPUs on spot instances.
Tech

React Server Components and HTMX Both Offer Less JS But One Team Quit

By Lucas Mendes/Jul 17, 2026

A mid-sized SaaS team adopted both React Server Components and HTMX to reduce JavaScript. Half the engineers quit within six months. Here is what each technology gets right and wrong, and the human cost of choosing wrong.
Tech

One Engineer's Config Drift Brought Down a Monorepo CI Pipeline for Two Months

By Deepa Iyer/Jul 17, 2026

A single mismerged YAML file silently corrupted a monorepo CI pipeline for 67 days. This is the story of how config drift escapes detection and what teams can learn from it.
Tech

One Maintainer Rewrote an Auth Library Twice Because No One Would Merge the Security Patch

By Sara Park/Jul 17, 2026

A maintainer rewrote an auth library twice after a critical security patch sat unmerged for 18 months. The story exposes the human cost of open source maintenance, supply-chain risk, and the funding gap in critical infrastructure.
Tech

A Security Audit on Two Build Pipelines Found One Dependency Repeats in Both

By Deepa Iyer/Jul 17, 2026

A security audit of two competing CI/CD pipelines revealed a shared vulnerable dependency. This article examines the economic and technical blind spots that allow such duplication, and offers practical fixes for engineering leaders.
Tech

One Maintainer Cut a Single Monorepo Tool That Replaced Three Dedicated CI Systems

By Yusuke Tanaka/Jul 17, 2026

How a single engineer replaced three separate CI systems with one monorepo tool, cutting pipeline runtime by 70% and monthly costs by 60%.
Tech

Flutter's Widget Tree vs SwiftUI's View Body Two Teams Paid for Both

By Deepa Iyer/Jul 17, 2026

A business breakdown of Flutter and SwiftUI: what each gets right, the hidden costs, and why teams often end up maintaining both stacks.
Tech

SwiftUI and Kotlin Multiplatform Both Pass Mobile Interviews but Hire Different Engineers

By Lucas Mendes/Jul 17, 2026

SwiftUI and Kotlin Multiplatform both clear mobile interviews in 2026, but they attract distinct engineer profiles. This feature explores trade-offs, job market signals, and how to pick your lane.
Tech

SwiftUI and Jetpack Compose Share One Syntax But Two Team Cultures

By Deepa Iyer/Jul 17, 2026

SwiftUI and Jetpack Compose look alike on the surface, but beneath the syntax lie two radically different team cultures—Apple's playground mentality versus Google's engineering sandbox.
Tech

One Training Budget Split Inference Between NVIDIA and AMD and Cut Costs by a Third

By Sara Park/Jul 17, 2026

Splitting inference across NVIDIA and AMD GPUs can cut costs by a third. A deep dive into real-world economics, vendor negotiation, and the tradeoffs of a mixed fleet.
Tech

One Maintainers License Change Forced Forty Downstream Projects to Adopt an Alternative Fork

By Yusuke Tanaka/Jul 17, 2026

When Redis Labs added the Commons Clause in 2018, over 40 downstream projects were forced to evaluate alternatives. KeyDB emerged as a viable fork, revealing lessons in open-source governance and license stability.
Tech

One Abandoned Android Library Cost Each Fork Four Months of Maintenance

By Yusuke Tanaka/Jul 17, 2026

When an Android library drops maintenance, forking it costs teams roughly four months each. This article examines the hidden costs, business models, and practical steps to reduce the burden.
Tech

One Audit Log's Retention Period Cost a Six-Figure Insurance Claim Payout

By Yusuke Tanaka/Jul 17, 2026

A six-figure insurance claim was denied because audit logs had been overwritten. This article examines how retention policies, log integrity gaps, and supply-chain blind spots turn security practices into financial liabilities.
Tech

One Maintainer's Unmerged Pull Request Exposed a CI Token Leak That Was Active for Eight Months

By Deepa Iyer/Jul 17, 2026

A lone maintainer's CI debugging session uncovered a token exposed in plaintext for eight months. The unmerged PR reveals systemic gaps in supply-chain security.
Tech

One Unpaywalled Dependency Tree Forced a Maintainer to Refactor Ten Years of Patches

By Deepa Iyer/Jul 17, 2026

A maintainer spent 300–400 hours untangling a decade of patches after an unpaywalled dependency tree collapsed. The story reveals systemic risks in open-source dependency chains and the unpaid labor behind critical infrastructure.
Tech

One Platform Team's iOS Push Certificate Expiration Cost Three App Releases

By Lucas Mendes/Jul 17, 2026

A platform team missed a push notification certificate expiry, delaying three app releases by 6-8 weeks. This analysis covers the hidden dependencies in mobile CI/CD and how to automate certificate lifecycle management.
Tech

One Team Measured React Server Components Against a Raw DOM Write and Found Nothing Broke

By Lucas Mendes/Jul 17, 2026

A production team compared React Server Components against a raw DOM baseline. Two weeks, 1.2 million sessions, and no regressions. Here's what they learned.
Tech

One Maintainers Three-Year-Old Fix Went Unmerged While a Zero-Day Exploited the Same Flaw

By Deepa Iyer/Jul 17, 2026

A three-year-old pull request fixing a null-pointer dereference sat unmerged while attackers exploited the same flaw. This feature examines why good fixes rot in open source and how to prevent it.
Tech

Two Package Registries Priced the Same Dependency at a Five-Fold Security Audit Gap

By Sara Park/Jul 17, 2026

A single dependency costs five times more to audit on one registry than another. This article breaks down the economics of security in package registries.
Tech

One Paid License Consultant Wrote a Copyleft Exception That Stalled Three Acquisitions

By Sara Park/Jul 17, 2026

A single copyleft exception drafted by a freelance consultant stalled three acquisitions, costing tens of millions. How one bad clause became a poison pill.