Monorepo boundaries: when shared code becomes shared pain
February 20, 2026
The promise
Monorepos sell a compelling story: one repository, shared code, atomic commits across packages. You extract a utility into a shared package, import it everywhere, and celebrate the DRY victory. For the first few months it works beautifully.
Where it breaks
The problem starts when the shared package becomes a gravity well. Every team adds their edge case. The "common" utils package grows to 200 exports, half of which are used by exactly one consumer. A type change in the shared package triggers CI for every downstream package. Builds slow down, PRs get noisy, and suddenly "shared" means "coupled."
Drawing the right lines
The fix isn't abandoning shared code. It's being deliberate about what crosses package boundaries. I follow a simple rule: only share things that change for the same reason. A date formatting function used by five packages? Shared. A validation schema that one team keeps modifying? Keep it local.
The other pattern that helps is treating internal packages like external ones. Version them, document breaking changes, and give consumers time to migrate. Tools like Turborepo and Nx make this practical with scoped builds and change detection, so you're not rebuilding the world on every commit.
The ownership question
Every shared package needs an owner. Not a "this team loosely maintains it" kind of owner, but someone who reviews every PR and says no when the abstraction is being stretched. Without ownership, shared packages decay into junk drawers that everyone depends on and nobody maintains.
The best monorepos aren't the ones with the most shared code. They're the ones where every package boundary exists for a clear reason.