Legora is the agentic operating system for legal work, supporting lawyers in research, review, and drafting across complex matters. It is used by more than 100,000 legal professionals at more than 1,800 leading law firms and in-house legal teams across over 50 markets. Engineering has kept pace. The monorepo nearly doubled in a year to over 600 projects and monthly CI runs grew sixfold – all supported by a platform team of three. Their monorepo is Nx-based, primarily in TypeScript, but also includes 43 Python projects, a Rust crate, a Java service, and protobuf checks, all run in a single DTE pipeline.
Challenge
Legora's growth outpaced the CI setup built to support it. Checks ran through a GitHub Actions matrix on Blacksmith, with fixed runner sizes per check and manually configured sharding (2x for functional tests, 6x for acceptance tests), a setup well suited to the team's size at the time. That workflow grew from 94 to 621 non-comment lines between March and May 2026. With distinct PRs up 35% in June alone, the static-matrix model wasn't just inconvenient – it was getting harder to maintain as the company scaled. Keeping it tuned by hand became a growing task for a three-person platform team, since nothing rebalanced automatically as new projects were added. The team's priority was stability first: making sure a fast-scaling engineering org could trust its pipeline before optimizing anything else.
Additionally, trust in the cache was a real blocker. By spring, the team had disabled caching for production builds and had written its own checker that rebuilt without the cache and compared output checksums, because cached and fresh builds occasionally disagreed.
Solution
Nx Cloud's task sandboxing replaced that guesswork with a trace of every file each task actually read and wrote. The first full run flagged 811 of 968 tasks. About six weeks later, the team had reduced sandbox violations to zero and first enabled strict mode. By mid-June, cache hit rate had risen from roughly 50% to 73% over two weeks.
I set out to drive our sandbox violations to 0 so we could actually trust our cache. What I didn't expect was all other problems it surfaced, config that was quietly clobbering itself, inputs that were subtly wrong. The whole repo came out healthier and faster on the other side.
Sofie ThorsenMember of Technical Staff, Legora
Legora then moved the core code checks onto Nx Agents: the affected-task fan-out of builds, typechecks, lint, unit, functional, and acceptance tests, all running on the Nx Cloud tenant the team already used as their remote cache. What went away was the hand-maintained matrix that used to assign each check to a specific runner.
It's about half the workflow config to maintain, and for the distributed checks it drops the manual sharding, per-check runner sizing, and required-check wiring that Blacksmith needs. As we add projects it re-balances on its own, whereas Blacksmith needs ongoing hands-on tuning.
Sofie ThorsenMember of Technical Staff, Legora
Nx Agents distributed task execution (DTE) is a task-distribution orchestrator, not a runner. Instead of assigning specific checks to specific runners up front, Nx decides continuously, task by task, which agent picks up which piece of work. A pool of agents sits ready, and as each one frees up, it picks up the next task in the queue. There's no shard boundary decided in advance, no manual mapping of a given check to a given runner size.
That continuous model does a few things on its own. Tasks that finish quickly don't sit waiting on a slower sibling task, and work keeps flowing to whichever agent is free. Assignment rules route CPU- or memory-heavy outlier tasks appropriately, so they don't cause out-of-memory failures on an undersized agent. When a task is detected as flaky, Nx retries it automatically on a fresh machine rather than failing the whole run. Task sandboxing enforces hermetic execution: every file a task reads or writes is traced, and undeclared access fails the task, so a cached result and a fresh build can't quietly diverge. And because Nx Cloud is orchestrating the run, it captures task-level utilization data (CPU and memory per task) for tuning, visibility a fixed matrix model doesn't provide.
As the codebase grows, none of this needs re-tuning: new projects and tests enter the same continuous stream without anyone adjusting shard counts or agent sizes.
| Capability | Orchestration | Runners Only |
|---|---|---|
| Task distribution | Orchestrated, continuous task assignment to agents as they free up | Static GitHub Actions matrix, fixed per-check assignment |
| Machine allocation | Dynamic sizing of machines depending on size of change | Fixed runner size per check, manually chosen |
Results
For the checks that moved to Nx Agents, CI configuration is roughly half what it was, and the manual sharding, per-check runner sizing, and required-check wiring those checks used to need are gone.
We run a lean Dx team of three people, so DTE's dynamic, continuous assignment is a better long-term fit for a monorepo our size than a static matrix, and it removes the class of problem where a slow shard idles the rest of the fleet.
Sofie ThorsenMember of Technical Staff, Legora
The DTE workflow now runs 16 targets across TypeScript, Python, Rust, and Java, plus seven repo-wide guardrails that did not exist in May: lockfile drift, generated-file sync, conformance rules, an auth ratchet, translation keys, protobuf compatibility, and Temporal bundles. All of that fits in fewer lines of config than the old matrix.
Reach out to our team!Scaling a monorepo with a lean platform team? Reach out to learn more about Nx Cloud.









