We Were Building Too Much
In the previous post, I talked about how we stopped building and started listening to our developers. This time, I want to dig into the actual engine that made it possible.
Our CI pipeline was a mess. Every commit triggered a full build of our monorepo. That meant running tests for services that hadn't changed, building libraries nobody touched, and waiting. Lots of waiting. Developers would push a one-line fix and then stare at a loading spinner for 45 minutes.
The Problem with Monorepos
Monorepos are great for code sharing. They're terrible for CI. The standard approach—build everything, test everything—doesn't scale. You end up with a pipeline that treats a typo fix like a major refactor.
We tried the usual solutions. Caching. Parallelism. Incremental builds. But nothing fixed the root cause: we were building things that didn't need building.
The Unified Monorepo Engine
So we built our own engine. The idea is simple: only build what changed. But the implementation? That's where it gets interesting.
Our engine tracks dependencies at the file level. When you push a commit, it figures out exactly which packages, services, and tests are affected. Then it builds only those. The rest? Skipped. Not cached—skipped.
Here's how it works:
- Dependency graph – We map every file to every package that depends on it.
- Change detection – Git diff tells us what files changed.
- Impact analysis – The graph tells us what to rebuild.
- Execution – We run only the necessary builds and tests.
Sounds obvious, right? But most CI systems don't do this. They rebuild entire monorepos because it's simpler. Our engine makes the complex simple.
Real Results
We cut build times by 80%. A typical commit now takes under 10 minutes. Developers get feedback faster, which means they ship faster.
But here's the cynical developer take: "Great, another tool that'll break in six months." Fair point. The engine relies on accurate dependency mapping. If you have circular dependencies or dynamic imports, it gets tricky. We handle most cases, but it's not perfect.
The Developer Feedback Loop
We didn't just build this in a vacuum. We talked to developers. They told us what was slow, what frustrated them, what they wished for. That feedback shaped every decision.
For example, developers hated waiting for integration tests that never ran on their changes. So we added a feature: the engine tags each test with the services it exercises. If your change doesn't touch those services, the test is skipped.
What You Can Do
You don't need to build your own engine. But you can apply the same principles:
- Profile your pipeline – Find out what's actually slow.
- Talk to developers – They know the pain points.
- Incrementalize – Start with the biggest bottleneck.
Our engine is open-source. You can find it on GitHub. But more importantly, you can learn from our mistakes.
The Future
We're working on smarter caching and better error handling. The goal is a CI pipeline that feels instant, even for massive monorepos.
But for now, I'm just happy that our developers can push code and get results in minutes, not hours. That's the win.