THE MIGRATION LINE
INCIDENT // COINBASE-2020IN PROGRESS

Coinbase had nearly a thousand services in separate repos, where a fix to a shared library could take months to roll out. So they bet on a Bazel monorepo.

COINBASE·2020·MONOREPO / BAZEL / BUILD / DEVELOPER-PRODUCTIVITY·✓ Sourced — blog.coinbase.com
System stress over timeNominal
100+ projectsIn monorepo
22%Daily deploys
Go, Ruby, Node, PythonLanguages
whole companyTarget
BASELINE

Nearly a thousand services, each in its own repo

By mid-2020 Coinbase ran nearly a thousand services across separate repositories, and shared libraries — logging, server boilerplate, authn/authz — were depended on by many or all of them. In that polyrepo world, every internal library had to be versioned and published, and every consuming team had to bump their version to pick up a change.

The other half of the pain was deployment. Coinbase’s proprietary tool Codeflow was built on the assumption that one GitHub repository equals one Docker image equals one deployment per AWS account. Features had been bolted on to allow more, but the result was a stiff, opaque chain from source code to release that got harder to learn with every addition.

REQUIREMENTS

What they actually needed from a code layout

Stated plainly, Coinbase wanted three things. Code sharing without a publish-and-version dance. A single source of truth so that a change to a widely-depended library immediately triggers the test suites of every dependent service. And an explicit, transparent way to define the relationship between source code and what actually gets deployed to their cloud — the thing Codeflow had made opaque.

Underneath all of it was a strategic requirement: to stop investing in internal, proprietary tooling and position the company on industry-standard tooling instead.

THE OPTIONS

Keep bolting onto Codeflow, or change the assumption

The path of least resistance was to keep extending the existing developer-productivity tooling — more Codeflow features to smooth over the polyrepo edges. But every new feature led to an even steeper learning curve, because each one added another proprietary relationship engineers had to understand. Coinbase describes choosing instead to put a pause on that tooling and head down the “contentious path of monorepo.”

THE CALL

Bazel, with a dependency graph and a shared cache

Coinbase chose a Bazel monorepo. In a monorepo there is, by definition, only ever a single version of a given library; any change to it is immediately rolled out to the whole company and its impact is measured by the monorepo’s test suite. Bazel supplies the piece that makes this survivable — an explicit build dependency graph, so a commit builds and tests exactly what it affects rather than the entire repository.

Because that graph still bottoms out in libraries almost everything depends on, even building one project could take an hour. So the real engineering was caching: making builds hermetic — deterministic outputs regardless of which machine or time of day runs them — so outputs can be aggressively cached and reused across CI and developer machines. That meant SHA-pinning or vendoring every dependency and keeping the host system out of the build.

CONSEQUENCES

What you sign up for when one repo holds everything

By the time of writing, the monorepo held over 100 projects and 22% of Coinbase’s daily deployments across Go, Ruby, Node, and Python, with a stated goal of migrating the whole company by early 2021. But collapsing every project into one repository broke Coinbase’s security model, which assumed one GitHub repo equals one project and was enforced at the repo level — leaving a “one size fits all” problem where the whole monorepo would be policed at the level of its most sensitive project. Their answer was to adapt their internal review-gatekeeper, Heimdall, to read a generated CODEOWNERS file rolled up from per-directory OWNERS files, so each directory could require its own reviewers.

The unfinished edges were named honestly. Developers still built on their laptops and couldn’t use the shared caches, so the plan was to give each engineer a remote Linux machine in EC2. Bazel had no strong support yet for React and React Native. And the tooling that scaled to ~100 projects was expected to face growth of over an order of magnitude, straining their caching, version control, and build strategies.

A monorepo doesn’t remove the hard problems of scale — it trades a thousand small versioning problems for one enormous caching, security, and tooling problem, and it only pays off if you actually solve that one.

Source — read the original

A plain-language, AI-drafted and human-edited retelling of the article published on blog.coinbase.com, reorganized and explained in our own structure and words, with original analysis in the editor's note above. The facts, numbers, and decisions belong to the original author and are not altered. For the full depth, read the source.

← All systems