From Xcode to Bazel: How Spotify Rebuilt Its iOS Build Pipeline
Spotify has been evaluating Bazel since 2017, but it wasn’t until 2020 that the build system became the clear path forward for the company’s iOS development. Facing a codebase growing more than 30% year-over-year, the client engineering team needed a unified build system that could scale across their polyglot, multiplatform repository. The goal was straightforward: cut down both CI and local build times, and shorten the feedback loop for more than 200 engineers.
The migration was completed without interrupting a single weekly release for millions of iOS users.
Why Bazel?
Spotify’s iOS app had relied on the default Xcode build system since its 2008 debut. That setup worked for years, but as mobile codebases ballooned in size and contributor count, the limitations became painful. Modern alternatives like Bazel (open-sourced from Google’s Blaze) and Meta’s Buck promised faster builds through remote caching and remote build execution (RBE).
With those promises in mind, Spotify began productionizing Bazel in early 2020. The objectives were explicit: reduce CI and local build times and boost developer productivity by accelerating feedback.
Running Two Build Systems Side by Side
Migrating a project with 120+ contributing teams required a safety net. Spotify’s approach was to run Bazel and Xcode in parallel until Bazel was fully validated. Previous investments in tooling made this possible — the project had already moved from a checked-in pxbproj file to a custom Ruby DSL and YAML format, allowing developers to declare new modules without directly editing Xcode project files.
FooKit:
type: spotify_static_library
sources:
- Sources/**/*.swift
resource_dirs:
- Resources
deps:
- BarKit
That abstraction layer proved critical. Scripts generated more than 2,000 BUILD.bazel files in a matter of days, with only 30 to 50 requiring manual authoring. Most engineers didn’t even notice the dual build system setup. The declarative module above automatically produced a corresponding gitignored BUILD.bazel file:
music_swift_library(
name = "FooKit",
srcs = glob(["Sources/**/*.swift"]),
data = music_glob(["Resources/**"]),
deps = ["//Systems/BarKit"],
)
Measuring the Impact
The results came quickly after CI configurations started moving to Bazel in mid-2022. At that point, developers were waiting roughly 80 minutes for pre-merge feedback.
Figure 1: Green represents seven-day 75th percentile moving average build time in minutes.
The migration prioritized the longest-running CI configurations. One particularly heavy setup — more than 800 test targets spanning nearly 3 million lines of code — dropped from over 45 minutes with Xcode to under 10 minutes with Bazel. The gains came primarily from an efficient remote cache and parallelization through remote build execution. Spotify uses BuildBuddy to run builds and gather telemetry, which helped pinpoint performance bottlenecks and improve cache hit ratios.
Further improvements arrived through increased module isolation and targeted test selection. Using Bazel queries and bazel-diff, the team could deterministically identify which parts of the dependency graph were affected by a change, running far fewer tests. Within months, CI feedback dropped from 80 minutes to 20 — a fourfold improvement.
The Hidden Costs of a Dual System
Maintaining two build systems isn’t free. While pre-merge build times improved dramatically for feature engineers, platform engineers spent more time debugging discrepancies between the two systems. Builds would pass locally but fail in CI, or vice versa, due to subtle differences in how Xcode and Bazel handled compilation.
The only permanent fix was to eliminate one of the two systems entirely.
Bringing Bazel Into Xcode
iOS engineers live in Xcode for building, debugging, and testing. Spotify needed a Bazel integration that supported existing workflows without forcing developers to leave their IDE. The team collaborated with the open source community on rules_xcodeproj, an integration project backed by several companies on similar migration paths.
Testing began in November 2022 as an opt-in A/B experiment.
Figure 2: Dark green represents Xcode build system users, and light green represents Bazel.
The rollout was gradual. Early adopters provided feedback and bug reports as the integration matured. By March 2023, nearly all iOS engineers were using the Bazel-based Xcode integration, with the 75th percentile build time hovering around 30 seconds. Local and CI builds were now running under the same system, which eliminated a whole category of "works on my machine" issues.
In May 2023, the legacy build system was decommissioned for local development. Only one critical piece of infrastructure remained: release builds.
Releasing With Bazel
Shipping a mobile app involves far more than compiling code. Spotify’s release process ties into experiment platforms, metadata registration, crash report collection, and countless custom integrations accumulated over 15+ years. Ownership of these pieces was spread across multiple teams, so the rollout required a centralized plan.
The team documented the strategy, risks, and rollback options in a single document shared with all stakeholders. Known differences between build systems were tracked, and risky changes were broken down across multiple releases.
During the transition, Spotify produced fully optimized release builds from both Bazel and Xcode on the same hourly cadence. This dual production ensured a hot-fix path back to the legacy system if needed. Automated tooling constantly compared the outputs to catch discrepancies:
- Assets: Images and other resources were compared to ensure nothing was missing or incorrectly bundled.
- Configuration files:
Info.plistand JSON files were checked for equality. - Binaries: Symbol-level comparison confirmed that the correct classes were compiled into the final application.
The Final Rollout
The release strategy started with employee devices for about two weeks, allowing end-to-end testing of the release tooling without disrupting the weekly schedule. From there, the rollout expanded to external alpha and beta testers. Since A/B testing multiple app builds isn’t feasible, only one build went out to users at a time.
Spotify continued producing builds under both systems for a few additional releases as a safety measure. Crash and performance metrics looked strong, with no significant issues attributed to the build system switch.
With this migration, Spotify’s iOS app became the company’s first major client fully built with Bazel — a milestone reached through extensive contributions to the open source build system, its rules, and custom integrations.



