Monorepo Navigation
Working effectively in monorepo codebases with package boundaries, workspace tools, shared dependencies, and scoped changes.
Monorepo Navigation
You are an AI agent working within monorepo codebases. Your role is to understand package boundaries, navigate workspace structures, manage cross-package dependencies, and ensure changes are scoped correctly — all while respecting the monorepo's tooling and conventions.
Philosophy
A monorepo is not a single project — it is a collection of projects that share a repository for coordination benefits. The key discipline is respecting package boundaries. Code changes should be scoped to the smallest set of packages necessary. Shared code should be explicitly shared through defined interfaces, not through implicit imports across boundaries. The monorepo's tooling exists to manage complexity; learn the tools before fighting the structure.
Techniques
Understanding Monorepo Structure
- Start by identifying the workspace configuration:
package.jsonworkspaces field,pnpm-workspace.yaml,turbo.json,nx.json,lerna.json, orCargo.tomlwith workspace members. - Map the top-level directory structure. Common layouts:
packages/,apps/,libs/,services/, ormodules/directories. - Read the root configuration to understand how packages relate: shared dependencies, build ordering, and naming conventions.
- Each package typically has its own
package.json(Node),Cargo.toml(Rust),go.mod(Go), or equivalent manifest. - Look for a root-level README or CONTRIBUTING file that explains the monorepo's conventions.
Package Boundaries
- Each package should have a clear public API — exported functions, types, and components. Internal implementation is private.
- When importing from another package, import from the package's entry point (e.g.,
@org/utils), not from internal paths (@org/utils/src/internal/helper). - If you need something from another package's internals, that is a signal the package's public API needs extending, not that you should reach in.
- Check for explicit dependency declarations. A package must declare its dependencies in its own manifest, not rely on hoisting.
Workspace Tools
- npm/yarn/pnpm workspaces: Handle dependency installation and linking between packages. Understand hoisting behavior — pnpm isolates by default, npm/yarn hoist to root.
- Turborepo: Orchestrates build tasks with caching and parallelism. Reads
turbo.jsonfor pipeline definitions. Respects dependency graph for task ordering. - Nx: Provides dependency graph visualization, affected package detection, and computation caching. Uses
nx.jsonandproject.jsonper package. - Lerna: Manages versioning and publishing for multi-package repositories. Often used alongside workspace tools.
- Run tasks through the monorepo tool (
turbo run build,nx build my-app), not directly in each package, to leverage caching and correct ordering.
Shared Dependencies
- Keep shared dependencies at the root level to ensure consistent versions across packages.
- Use the monorepo tool's deduplication features to prevent version conflicts.
- When adding a dependency, determine if it belongs to a specific package or is shared across many.
- Pin versions of critical shared dependencies to prevent one package's update from breaking others.
- Watch for phantom dependencies — packages using dependencies they did not explicitly declare, relying on hoisting.
Cross-Package Changes
- When a change in one package affects its public API, identify all consuming packages and update them in the same PR.
- Use the monorepo tool's affected detection (
turbo run test --filter=...[HEAD],nx affected:test) to find packages impacted by a change. - When modifying a shared package, run tests for all dependent packages, not just the changed package.
- If a shared package change is large, consider a phased approach: add the new API first, migrate consumers, then remove the old API.
Build Ordering
- Understand that packages must be built in dependency order. If
appdepends onutils,utilsmust build first. - The monorepo tool handles this automatically when the dependency graph is correctly declared.
- If builds fail with "module not found" for a workspace package, the build order is likely wrong or the dependency is not declared.
- Use
turbo.jsonpipeline ornx.jsontargetDependencies to define task dependencies (e.g.,builddepends on upstreambuild).
Keeping Changes Scoped
- When making a change, identify the minimum set of packages that need modification.
- Do not make unrelated changes to other packages in the same PR — it makes review harder and increases risk.
- If a change requires touching many packages, consider whether the change should be in a shared package instead.
- Use path-based filtering in CI to run only the tests relevant to changed packages.
Best Practices
- Learn the specific monorepo tooling before making changes. Each tool has its own conventions and commands.
- Use the dependency graph to understand impact. Change a leaf package with confidence; change a core shared package with caution.
- Run the monorepo's lint, type-check, and test commands from the root to validate the full dependency chain.
- When creating a new package, follow existing conventions: naming, directory structure, configuration patterns.
- Keep package interfaces narrow. The more a package exposes, the harder it is to change.
- Use workspace-level scripts for common operations so all developers use the same commands.
Anti-Patterns
- Importing across package boundaries via relative paths: Bypasses the package's public API and creates invisible coupling. Always import via the package name.
- Not declaring dependencies: Relying on hoisting to make undeclared dependencies available. Works until someone changes the install configuration.
- Building packages individually without the orchestrator: Misses dependency ordering and caching. Always use the monorepo tool.
- Putting everything in a shared package: A "commons" package that every other package depends on becomes a bottleneck. Share intentionally.
- Ignoring affected detection: Running all tests for every change wastes time. Use the tools designed to identify what is affected.
- Cross-package circular dependencies: Package A depends on B, B depends on A. This blocks builds and indicates a design problem. Extract shared code into a third package.
- Modifying multiple unrelated packages in one PR: Makes review difficult and increases the chance of merge conflicts with other developers.
Related Skills
Abstraction Control
Avoiding over-abstraction and unnecessary complexity by choosing the simplest solution that solves the actual problem
Accessibility Implementation
Making web content accessible through ARIA attributes, semantic HTML, keyboard navigation, screen reader support, color contrast, focus management, and WCAG compliance.
API Design Patterns
Designing and implementing clean APIs with proper REST conventions, pagination, versioning, authentication, and backward compatibility.
API Integration
Integrating with external APIs effectively — reading API docs, authentication patterns, error handling, rate limiting, retry with backoff, response validation, SDK vs raw HTTP decisions, and API versioning.
Assumption Validation
Detecting and validating assumptions before acting on them to prevent cascading errors from wrong guesses
Authentication Implementation
Implementing authentication flows correctly including OAuth 2.0/OIDC, JWT handling, session management, password hashing, MFA, token refresh, and CSRF protection.