All posts

Context is King. Long Live the Monorepo.

Published Feb 11, 2026 · Martín Fernández

Last week we shipped session interrupt support. One feature. Twenty-five files changed across six packages: the API, the frontend, the workspace daemon, the protocol layer, the generated API client, and the end-to-end tests. Over a thousand lines, all in one pull request. An agent working across multiple repositories would have needed six separate contexts, six separate PRs, and a human to stitch the contracts together. In the monorepo, the agent saw the full picture and shipped it coherently.

That's the argument for how we structured Dap from the start.

Context radius

There's a concept we keep coming back to: the context radius—how much of the system an agent can see and reason about in a single session. Every architectural decision either expands or shrinks that radius.

Spread your code across repositories, and the agent sees fragments. It can fix a bug in the API but miss that the same pattern exists in three other services. It can refactor a function but break the contract with the package that depends on it. The context radius is one repo at a time.

A monorepo collapses that problem. One clone. One context window. Every type, every service, every deployment manifest—visible in a single conversation. The context radius becomes the whole system.

TypeScript as lingua franca

We needed a language that could span frontend, backend, CLI tooling, and infrastructure automation. TypeScript won because of the type system—not because it's strict, but because it's expressive enough to describe contracts that travel across boundaries.

This matters for context radius. When an agent modifies a type in workspace-protocol, the compiler immediately flags every consumer that breaks—across the API, the workspace daemon, the frontend client. The agent doesn't need to search for dependents or guess at contracts. The type system makes the full blast radius visible, and the monorepo ensures all of it is reachable in the same session.

A type defined in a shared package flows through the API, into the generated client, into the frontend, and back again without losing its shape. No runtime surprises. No schema drift. The monorepo makes this possible; TypeScript makes it enforceable.

The shape of the repo today

Right now: 9 applications, 23 shared packages, Terraform modules, Helm charts. Application code, shared libraries, infrastructure, deployment configuration—all in one tree.

The numbers keep moving. A few weeks ago we had 18 packages. Now 23. Not because we're splitting for the sake of splitting—because patterns emerge that deserve their own boundary. git, crypto, jwt, and object-storage all started as utility code buried inside the API. Once a second consumer appeared, extraction was a single PR that touched 105 files across the entire graph. In a multi-repo setup, that's a coordinated release across four new repositories with versioning, publishing, and downstream dependency updates. In the monorepo, it was Tuesday.

When an agent needs to understand how a feature flows from the UI to the database to the Kubernetes pod, it doesn't context-switch between repositories. The full path is inside the context radius.

Pipelines and automation

Five GitHub workflows handle the lifecycle:

  • ci.yml – lint, type-check, test across all packages
  • cd.yml – build and deploy on merge
  • ci-terraform.yml – validate infrastructure changes
  • devcontainer-release.yml, devcontainer-cache.yml – manage development environments

Turborepo orchestrates builds with dependency awareness. If @dap/runtime changes, only packages that depend on it rebuild. In theory.

There used to be a sixth workflow for Coder-based development environments. We removed it when we dropped the Coder and Docker workspace providers entirely. That's the monorepo giving back: when you remove a capability, you can trace every dependency, delete every reference, and know the graph is clean. One refactor, one PR, gone.

The scaling tension

AI makes a lot of changes. Small ones. Frequent ones. And CI doesn't know the difference between a meaningful change and noise.

The monorepo creates pressure in two directions:

  1. Change detection complexity. Turborepo helps, but determining what actually changed—and what needs to re-test—is harder than it looks. A type change in a shared package might affect nothing. Or everything. The tooling doesn't always know.

  2. CI cost compounds. Every pull request runs against the full graph. When agents open dozens of PRs per day, the feedback loop slows down. You either pay for parallelism or wait.

We started pushing back. A recent CI consolidation rewrote the pipeline from 289 lines to 123, cutting billable minutes by collapsing redundant jobs. It's not a solution—it's the first acknowledgment that the monorepo's CI surface area needs active management, not just Turborepo defaults.

The tradeoff

Monorepos trade one kind of complexity for another. You lose the isolation of separate repositories. You gain a single source of truth.

Whether the trade is worth it depends on your context radius requirements. If your agents work on isolated services with stable contracts, separate repositories might be fine—the radius doesn't need to be wide. But if your agents routinely cross package boundaries, modify shared types, or ship features that span the stack, a narrow context radius means a human has to fill the gaps.

The question isn't "monorepo or not." It's: how much of the system does your agent need to see at once to do its job without a human stitching pieces together?

For us, building with AI, that answer keeps getting bigger. The last week made it concrete: a cross-stack feature, a multi-package extraction, a CI overhaul, a provider removal—each one touched corners of the system that would have been invisible across repository boundaries. The monorepo didn't make any of it easy. It made all of it possible in a single conversation.