System designJune 1, 20264 min read

5 Years Ago: A Mid-Level Engineer’s Shock Upon Inheriting a Monorepo "Anchor"... Powered by Bazel

5 Years Ago: A Mid-Level Engineer’s Shock Upon Inheriting a Monorepo "Anchor"... Powered by Bazel

Five years ago, when I was still a mid-level engineer used to simple repositories (polyrepos) where running code was as trivial as npm run dev or go run main.go, my team was handed over a massive cluster of projects.

The Tech Lead from the outbound team handed over the repo with a piece of advice whose sheer "heavyweight" nature I didn't fully grasp at the time: "Everything is in a single repo. We use Bazel to build it."

I eagerly ran git clone, excitedly opened up the codebase, and... completely froze. There were no familiar script files. Staring back at me were dozens of files named WORKSPACE and BUILD.bazel scattered across every single directory. That was the beginning of a grueling yet most rewarding chapter in my career.

1. The Culture Shock of "Explicit Dependencies"

To a mid-level engineer back then, my mindset was simple: if I wanted to use a library, I just imported it; if it was missing, I added it to package.json or go.mod. But stepping into the world of Bazel, that rule was completely shattered.

Bazel operates on a strict philosophy: Hermetic Computing. This means it does not trust anything pre-existing on your local machine.

  • Want a file in service-a to call a helper function in shared-lib? You couldn't just type a relative path. I had to go into the BUILD.bazel file of service-a and explicitly declare: "I am going to depend on the target //packages/shared-lib."

  • Forgot to declare it? Bazel would immediately throw an error at build time, even if your IDE recognized the code perfectly fine.

During the first week of the handover, I spent half my time just... learning Starlark (a Python-like language) to write those BUILD files. It was incredibly frustrating, and I kept asking myself, "Why on earth do people make things so hard on themselves?"

2. And Then... The "Magic" Happened as the System Expanded

Once I climbed over the configuration hurdle and got into the groove of fixing bugs and shipping features, I realized Bazel wasn't a tool for self-inflicted torture—it was an engineering masterpiece designed for large-scale monorepos.

The system we inherited had nearly a dozen services of various sizes. In a typical CI/CD pipeline for a monorepo, every push would mean waiting an hour for the system to re-test and re-build everything. With Bazel, it was a completely different story:

Next-Level "Affected Builds" and "Caching"

Bazel maps out the Dependency Graph of the entire repository like the back of its hand.

  • When I modified a single line of code in service-frontend-a, Bazel knew perfectly well that this change had absolutely nothing to do with service-backend-b or service-mobile.

  • Upon running the build command, it only built service-frontend-a.

  • As for the rest of the services? Bazel pulled them from the cache (even a Remote Cache shared between my machine and the CI/CD server) at speeds measured in milliseconds.

Bazel’s tagline rings true: "Fast and Correct. Choose two." With Bazel, you actually get both. A build is reproducible and consistent; the dreaded "It works on my machine but breaks in production" phrase simply vanished.

A First Taste of a Polyglot Monorepo

The project handed over to us wasn't built on a single language stack. It had a TypeScript frontend, a core service in Go, and several automation scripts in Python.

Without Bazel, our team would have had to install multiple runtimes (Node, Go, Python) locally and deal with painful environment management. With Bazel, it automatically fetched the exact versions of Go and Node into isolated hidden directories and ran them. I only needed to execute a single command: bazel test //..., and all test cases across all three languages ran concurrently and flawlessly.

Epilogue

Five years ago, taking over a monorepo managed by Bazel felt like a driver used to a small-displacement manual car suddenly being thrown into the cockpit of a Boeing 747. It was overwhelming—the control panel was packed with buttons, and a single mistake in a config file could ground the entire "airplane."

Yet, that trial by fire with Bazel is exactly what forged an incredibly disciplined mindset in me regarding Dependency Management and Build Systems. It forced me to look under the hood of the architecture: to understand how my code is actually compiled and how CI/CD pipelines truly operate. It became an invaluable stepping stone, ensuring that I would never be intimidated by any enterprise-grade, massive-scale system ever again.

Related reading