This article is written by Marijan Kozic
Your CI pipeline worked fine when the app was young. Then the app grew. Features got split into modules. Teams formed around those modules. And somewhere along the way, what used to be a 4-minute build became a 25-minute one. Then 35. Now nobody pushes to main before lunch because the queue is already backed up.
If this sounds familiar, you are not alone. According to Google’s 2024 Developer Survey, 83% of Android apps over 500,000 lines of code struggle with build performance. The problem compounds as your app matures: the more you invest in modular architecture to manage complexity — which is the right thing to do — the more you expose the fundamental bottleneck of building everything sequentially on a single machine.
The solution is not faster hardware. It is a different build topology. This post explains what that topology is, why it works, and how to implement it on Codemagic.
What the problem actually looks like
Consider a fintech super-app. The term is well established in Southeast Asia and Latin America, and increasingly used in the West to describe apps that bundle multiple distinct services under one roof: payments, lending, insurance, loyalty, onboarding. Each service is owned by a separate product team, built as a miniapp or feature module, and assembled by a host application into a single release binary.
The CI pipeline for such a product typically looks like this:
- Developer pushes to main
- CI checks out the repository
- CI builds Module A… finishes
- CI builds Module B… finishes
- CI builds Module C… finishes
- (repeat for all remaining modules)
- CI assembles the host app
Every step waits for the previous one. If you have 10 modules averaging 8 minutes each, that is 80 minutes of sequential work before your host app even starts building — even though most of those modules have no dependency on each other and could all run simultaneously.
A real-world case study on ProAndroidDev describes a 96-module Android app where CI builds had ballooned to 47 minutes. By enabling parallel module compilation and restructuring the pipeline, the team cut that to 18-20 minutes on the same hardware. Separate research into modularised Android builds found CI pipelines running 3x faster after moving from sequential to parallel builds. With cross-machine distribution the improvements go further still.
The pattern that fixes it: fan-out/fan-in
Fan-out/fan-in is a build topology that matches the shape of your code. Independent modules build in parallel (the fan-out). When all of them finish, the host app assembles their artifacts (the fan-in).
The total pipeline time is now determined by the slowest single module, not the sum of all modules. In the 10-module example, 80 minutes of sequential work becomes roughly 8-12 minutes of parallel work, depending on module build times and how many concurrent machines you provision.
There is a secondary benefit that engineering managers tend to appreciate as much as the speed gain: module builds are independently observable. If Module D fails, you know immediately which team’s code broke and exactly what the error was. Failure is isolated, attribution is clear, and the teams whose modules passed are not blocked from continuing their work.
How to implement it on Codemagic
There are three approaches, each suited to a different team context. We have detailed implementation guides with full working code in our documentation, so this post focuses on the practical picture rather than reproducing every YAML block and script.
Approach 1: Pure Codemagic with the REST API
The most universally applicable approach requires no external tools. A lightweight coordinator workflow, triggered by a git push, calls the Codemagic REST API to start all module builds simultaneously. It polls each build until completion, collects the artifact URLs from the finished builds, and triggers the host app build with those URLs injected as environment variables.
This works for any platform — Android, iOS, Flutter, React Native — because each module workflow is a standard Codemagic build. The coordinator itself is a short Python script (Python 3 is pre-installed on all Codemagic instances) running on a lightweight Linux VM.
The trade-off is that this approach builds every module on every push by default. You can address this with Codemagic’s when: changeset condition, which skips a module build automatically when none of its source files changed since the last successful build. It is not as sophisticated as true dependency graph analysis, but it handles the common case of a developer working in a single module without touching the rest.
This approach is the best fit for teams whose modules span multiple platforms — a native Android module, an iOS framework, and a shared React Native bridge, for example. Each module workflow is independently configured with the right instance type, signing setup, and build commands.
Approach 2: Nx Cloud Distributed Task Execution on Codemagic VMs
If your monorepo uses Nx, you can hand the orchestration problem to Nx Cloud’s Distributed Task Execution (DTE) and stop maintaining a coordinator script.
Nx builds a project graph of your entire workspace. It knows that Module C depends on Module B, and that Module A and Module B have no relationship to each other. From that graph it derives a task execution plan: run A and B in parallel, then run C once B is complete, then assemble the host app when everything finishes. You do not write any of that sequencing logic. It comes directly from your dependency declarations in package.json and project.json files.
The part that makes this particularly powerful is nx affected. When you push a commit, Nx compares your changed files against the base branch and walks the project graph to identify every module that could be impacted — both directly changed modules and anything that depends on them. It then builds only those modules and skips the rest entirely. Push a change to Module B, and Nx builds Module B and the host app. Module A and Module C do not run at all. On a large module graph, this can eliminate the majority of build work on any given commit.
Nx Cloud DTE takes this a step further by distributing the remaining work across multiple machines. The way it works: Nx Cloud acts as a task broker sitting between your coordinator VM and your agent VMs. Your coordinator submits the task graph to Nx Cloud and then blocks, waiting for results. Your agent VMs connect to Nx Cloud and announce themselves as available. Nx Cloud assigns tasks to agents based on what is ready to run (respecting the dependency order), tracks completions, and feeds the results back to the coordinator. When the last task finishes, the coordinator receives the all-clear and the pipeline continues to the host app assembly step.
Crucially, Nx Cloud holds tasks in a server-side queue and dispatches them dynamically. If an agent VM takes 45 seconds to provision, check out the repo, and run npm ci before calling npx nx-cloud start-agent, that is fine — the tasks will be waiting for it. If three agents are all free and there are seven tasks ready to run, three of them get dispatched immediately. When one agent finishes its task and another batch is ready, the tasks flow to whichever agents are free. The scheduling is greedy and continuous; there is no static assignment of “agent 1 handles modules A-C, agent 2 handles modules D-F”.
To use this setup with Codemagic, the coordinator workflow starts by triggering the agent workflows via the Codemagic REST API, passing a shared NX_CI_EXECUTION_ID environment variable (set to Codemagic’s own CM_BUILD_ID) so Nx Cloud can group all the VMs into a single pipeline execution. The coordinator then runs the Nx commands. The agents check out the repo, install dependencies from the warm cache, and sit in the npx nx-cloud start-agent polling loop until Nx Cloud assigns them work or the run is complete. The whole setup fits in codemagic.yaml without any external script file for the orchestration part.
What you get from this combination: Nx handles what needs to be built (change analysis and dependency ordering), Codemagic provides the machines, and Nx Cloud connects them. Each component does what it is good at.
Approach 3: Single VM with Nx or Turborepo
If builds are fast enough and you want to keep things simple, both Nx and Turborepo can run change-aware parallel builds on a single Codemagic VM without any multi-machine coordination. Nx parallelises tasks across available CPU cores respecting dependency order and skips unchanged modules via nx affected. Turborepo does the same with a simpler configuration model.
This is the right starting point for smaller teams or monorepos where individual module builds take under two or three minutes. The nx affected behaviour alone — skipping modules that did not change — often cuts pipeline time by more than half on incremental pushes. You can always migrate to multi-machine DTE later, since the Nx workspace configuration carries over unchanged.
The iOS question
Here is something worth being direct about. Nx Cloud’s own hosted compute — the machines Nx Cloud provisions when you use their fully managed DTE service — offers Linux resource classes only. No macOS. For a pure Android or web monorepo, that is fine. For any team building iOS apps, it means Nx Cloud’s hosted agents cannot be used for iOS module builds at all.
When you use Codemagic as your agent compute provider alongside Nx Cloud DTE, this is a non-issue. Your agents are Codemagic Mac mini M2 or M4 VMs with Xcode pre-installed, running on Apple Silicon hardware, with full access to Codemagic’s automatic code signing via the Apple Developer Portal API. Each module build — whether it produces an Android AAR, an iOS XCFramework, or a React Native bundle — runs on the same class of machine with the same toolchain already in place. For teams building a cross-platform super-app, using Codemagic VMs as Nx Cloud agents is not just convenient. For the iOS side of the build, it is the only path that works.
What This Actually Costs
Let’s work through a realistic scenario. Imagine a team of 15 developers working on a React Native super-app split into 12 modules. They push code to main or open PRs roughly 25 times a day across 22 working days—amounting to approximately 550 CI pipeline executions per month.
Here is how the infrastructure and financial math break down for a Manual DTE (Distributed Task Execution) architecture.
1. Nx Cloud Costs
Nx Cloud uses a credit-based model for its platform and orchestration features.
- CI Pipeline Executions: Every time your CI provider spins up a workflow that calls Nx, it registers as a pipeline execution costing 500 credits. Because we are using Manual DTE (where our own Codemagic VMs do the heavy lifting), Nx Cloud charges zero compute minutes. At 550 executions per month, that is $550 \times 500 = 275,000$ credits consumed.
- The Credit Bill: The Team plan includes 50,000 free credits monthly, leaving 225,000 in overages. At $5.50 per 10,000 additional credits, this comes out to $124/month.
- Active Contributor Fees: Nx Cloud charges $19 per active contributor per month (with the first 5 free). Because Codemagic triggers pipelines on git events, Nx evaluates the actual developers authoring the commits. For 15 developers (10 paid seats), this adds $190/month.
Nx Cloud Monthly Total: ~$314 / month
(Note: In months where nx affected successfully keeps changes isolated to a few modules, overall execution times decrease, but your baseline platform cost remains highly predictable).
2. Codemagic Costs
By leveraging an annual, flat-rate plan on Codemagic, you eliminate the volatile per-minute billing typically associated with macOS runners.
- The Base Plan: A Codemagic M4 annual plan starts at $5,400/year, providing 3 concurrent slots and unlimited build minutes on Apple Silicon.
- The Parallelism Add-On: To run one main coordinator pipeline plus 3 concurrent agent VMs simultaneously, you need 4 concurrent slots. Adding 1 extra concurrency slot costs $1,800/year.
Codemagic Monthly Total:
$7,200/year, broken down as $600 / month for unlimited iOS and Android builds.
The Full Stack Cost
| Infrastructure Component | Cost Per Month |
|---|---|
| Nx Cloud (Orchestration & Cache) | $314 |
| Codemagic (Unlimited macOS Compute) | $600 |
| Total Stack Investment | $914 / month |
For less than $61 per developer per month, you have completely decoupled your super-app’s scaling from your CI budget.
What That Investment Buys You (The Real ROI)
Before introducing Manual DTE, your developers were stuck sitting through 25-minute sequential CI pipelines on every single push. After implementing a 1 → 3 worker fan-out pattern, a conservative target build time drops to 10 minutes.
While saving 15 minutes of raw clock time per build is impressive, the true productivity savings go much deeper when you account for human engineering behavior.
The 3 Pillars of Reclaimed Time
- Direct CI Wait Time Reduction: Saving 15 minutes across 550 builds directly recaptures 137.5 hours of pipeline execution time per month.
- Eliminating the Context-Switching Tax: A 25-minute build forces an engineer to context-switch to another task, destroying their cognitive flow. A 10-minute build allows them to stay in the zone. Assuming developers actively wait on PR feedback loops for roughly half of those 550 builds, avoiding a 15-minute mental ramp-up loop reclaims an extra ~68 hours/month across the team.
- The Shared Remote Cache Effect: Connecting your monorepo to Nx Cloud unlocks remote caching. If an engineer or the CI pipeline compiles a shared framework module once, the other 14 developers pull it instantly from the cloud rather than rebuilding it locally. If each dev hits the remote cache just twice a day, that saves another ~50 hours/month of local spinning wheels.
The Final Ledger
When you aggregate the direct CI savings, avoided context-switching, and local remote cache hits, this setup reclaims over 250 engineering hours per month across a 15-person team.
Total savings
250 hours × USD 75/hour (fully loaded cost) = USD 18,750 in monthly engineering capacity
By spending $914/month to pair Nx Cloud’s orchestration with Codemagic’s predictable macOS pricing, your organization isn’t just optimization-gazing—it is buying back nearly $19,000 worth of pure, focused engineering velocity from the ether every single month.
When this approach is the right fit
Fan-out/fan-in adds meaningful complexity to your CI configuration. It is worth the investment when sequential builds are genuinely blocking your team. It is probably overkill for:
- Apps that are not yet modularised, where a single module is simply slow to compile
- Small teams pushing infrequently, where queue time is rarely a bottleneck
- Projects where modules are tightly coupled and must rebuild together anyway — parallelising them saves less than you might expect
A practical threshold: if you have 5 or more independently buildable modules and your pipeline regularly exceeds 15 minutes, fan-out/fan-in will likely pay for itself in recovered developer time within the first month.
The bottom line
Modular architecture is the right call for any large mobile product. But modular code on a sequential CI pipeline means paying the full cost of complexity while leaving the main benefit — parallelism — on the table.
Fan-out/fan-in aligns your CI topology with your code topology. Independent things build independently. Everything waits only for what it genuinely depends on. On Codemagic, you can implement this today with no changes to your application code, using Apple Silicon machines for iOS and Android alike.
The full implementation guide covers all three approaches with complete working code examples.
