Summary
The monorepo is a coordination choice. It lets us change the data contract, the automation that acts on it, the review surface that handles ambiguity, and the documentation that preserves the decision as one reviewable unit. Physical proximity is useful because the operating workflows are still more coupled than their package names suggest.
Problem
Real operating work does not arrive pre-cut into deployable services. A marketing workflow may depend on lakehouse facts, scheduling, delivery, suppression policy, review state, and an operator console. The person using it experiences one outcome. The implementation crosses several responsibilities.
Splitting those responsibilities into separate repositories too early would not remove the coupling. It would convert it into version coordination, distributed deployments, duplicated context, and contracts frozen before we understand the actual workflow. Keeping everything together without structure fails in the opposite direction: the repository becomes a large room where anything can reach anything.
Principle
We keep conceptually separate responsibilities physically close until their boundaries are proven by repeated use. Package ownership, typed contracts, tests, and repository hooks enforce the separation that deployment topology does not. The goal is not one repository forever. The goal is to avoid paying distributed-systems costs for boundaries we have not yet earned.
Platform Shape
The repository contains applications, services, libraries, integrations, pipelines, jobs, infrastructure, operational support, and documentation. A change can therefore move through the complete causal chain: a source fact is normalized, a capability interprets it, a job advances the work, a surface shows the result, a guardrail protects the boundary, and a document explains what future maintainers are allowed to assume.
That is the useful property. "Monorepo" by itself proves nothing. If an API route reaches directly into analytics, if every job embeds provider calls, or if state semantics exist only in UI code, physical togetherness has merely made the shortcuts easier.
So we use the repo as a control surface. app/ adapts runtime requests; services/ owns capabilities and state transitions; libs/ carries shared contracts and deterministic policy; integrations/ translates external systems; pipelines/ moves data reproducibly; jobs/ exposes orchestration; and docs/ carries architectural memory. Tests and hooks turn some of those claims into executable constraints.
What happens when a boundary eventually deserves its own database or deployment cadence? The move should follow an existing contract. It should not require us to discover, during the migration, that half the company depends on an unrecorded join.
Tradeoffs
The monorepo makes coordinated changes cheaper and accidental coupling cheaper too. That is the bargain. We have to spend attention on package rules, source ownership, tests, and documentation because the filesystem will not protect us by itself.
We also accept that some changes touch several layers. That larger diff is not necessarily a design smell; sometimes it is the honest shape of an operational change. The discipline is to keep each responsibility explicit, so closeness improves coordination without erasing boundaries.