For monorepos

Using AI coding agents efficiently in a monorepo

The short answer

In a monorepo, a single change to shared code can affect many packages or services at once. AI coding agents can answer cross-package questions more reliably when they can follow explicit import and call relationships across package boundaries, rather than relying on text search alone.

Why a monorepo is a graph problem

The thing that makes a monorepo hard is precisely the thing a graph stores: which package imports which, and what crosses a boundary that was supposed to be internal.

RepositoryCodeFileCodeFileCodeFunctionCodeFunctionCodeClassCodeImportCodeChunkCodeChunkCodeChunk

Why monorepos are structurally different

A monorepo typically holds multiple applications and packages side by side in one repository: a web app, an API, a background worker, and a set of shared libraries between them, for example. Cross-package relationships are the norm rather than the exception: code in one application routinely depends on code maintained by a different team, several directories away.

Shared modules and shared APIs

Shared modules, common types, utility functions, API clients, configuration, exist precisely so multiple applications don't duplicate logic. That efficiency comes with a tradeoff: a change to one shared module can quietly touch every package that imports it, not just the package where the change was made.

Dependency graphs across package boundaries

Understanding a monorepo means understanding its import graph: which packages depend on which others, and through which specific imports. Text search can find files that mention a name; it can't reliably tell you which of those mentions is an actual dependency versus an unrelated match.

Context scope: how much of the monorepo does a question touch?

Most questions in a monorepo don't actually concern the whole repository; they concern one package and whatever depends on it. The challenge is identifying that scope correctly. Too narrow, and an agent misses an affected service; too broad, and it burns tokens reading packages that were never in the blast radius.

Platform team
A shared type used by three services looks harmless to change until one of those services breaks in production. The question isn't whether to change it; it's knowing, before making the change, exactly which services need to be checked.

The central question

“If I modify this shared type, which services may be affected?”

This is one of the most common, and most consequential, questions in a monorepo. Answering it well means tracing every direct reference to that type across package boundaries: every import, every function signature that uses it, every module that re-exports it.

Structural parsing that tracks imports, exports, and call relationships can trace a shared type or function to the files that directly reference it, across package boundaries, not just files that happen to contain similar-looking text. That's meaningfully different from grepping for a type name, which returns every textual match with no indication of which ones are genuine dependencies.

relationship graph
packages/shared-types/User.ts
  ├─ imported by → apps/api/handlers/createUser.ts
  ├─ imported by → apps/worker/jobs/syncUser.ts
  └─ imported by → apps/web/lib/userClient.ts
A limit worth knowing
Static structural analysis traces explicit references: imports, calls, re-exports. It can miss relationships introduced dynamically, such as string-based lookups or highly dynamic language features. It's a strong signal for scoping a change, not an exhaustive guarantee that every affected service has been found.

Where CodeMesh fits in

CodeMesh is a context optimization layer for AI coding agents. It helps agents understand a codebase without repeatedly searching, opening and reading large amounts of source code, allowing them to work with more precise repository context and dramatically fewer unnecessary tokens.

For a monorepo specifically, that means an agent can ask a direct question about a shared type or module and get back the packages that actually reference it, instead of re-deriving that answer by searching and reading files across every application in the repository.

Not quite. Text search returns every file where a name appears as text, with no distinction between an actual import and an unrelated match (a comment, a similarly-named local variable, a different type with the same name). Structural retrieval follows the actual import and call relationships, so results are direct references rather than text matches.

See how structural relationships hold up in your monorepo

Start Saving TokensSee the Benchmark