Cohesion first, distribution later
A modular monolith keeps related behaviour close, shared types honest, and failure modes inspectable while the product is still learning what matters. Clear module boundaries, explicit contracts, and a single durable data store usually beat a premature mesh of services.
Introduce independently deployed services when operational evidence demands it: different scaling profiles, separate release risk, or team ownership that cannot share a lifecycle. Until then, invest in the seams—packages, interfaces, schemas, and tests—that make a later split deliberate rather than remedial.
Contracts are architecture
External HTTP surfaces deserve OpenAPI definitions. Structured payloads and AI outputs deserve JSON Schema. Domain events deserve a documented shape before they become tribal knowledge.
Architecture is the set of decisions that remain expensive to reverse. Prefer those decisions to be visible in typed modules, validated boundaries, and ADRs—not hidden in informal service sprawl.
