About this article
This article is the second deep dive in the “Software Architecture” category of the Architecture Crash Course for the Generative-AI Era series, covering how to choose an application’s overall structure.
“How many pieces, integrated or separated?” is the least reversible decision in software architecture. The article compares the three patterns (monolith / microservices / modular monolith) and shows the modern default: “start as modular monolith, carve out as needed.”
What is overall structure in the first place
Overall structure is, in a nutshell, “the basic policy of how many pieces to split an application into and how they interact.”
Imagine running a restaurant. A small shop is most efficient with one owner doing all the cooking, serving, and checkout (monolith). For a chain, splitting cooking, serving, and checkout among specialists coordinated via manuals (microservices) scales better. In between, you can clearly divide the kitchen, floor, and register within one shop and prepare to spin them off if needed (modular monolith). Software is the same: you need to pick the right division method for your scale and operational capacity from the start.
Why choosing overall structure matters
What happens if you proceed without thinking deeply about overall structure? Trying to split a single app into multiple later means rewriting at scale — practically close to a rebuild. Conversely, consolidating multiple separate apps into one is heavy work involving data migration and downtime.
Overall structure is the precondition for every other technical choice. Leaving it vague forces redoing every downstream decision: framework, language, headcount, cloud, test strategy, deployment — all depend on it. For a single app vs functions split into integrated services, the engineer count and skill mix differ by an order of magnitude. What 5 people build as the former might need 20 as the latter.
“Build now, split later” is theoretically possible but essentially a rebuild in practice. Underestimating it hurts.
”Split or together” cannot be undone
Trying to split a single app into multiple later means rewriting at scale — practically close to a rebuild. Conversely, consolidating multiple separate apps into one is heavy work involving data migration and downtime. Decide deliberately at requirements time, with future scale and operational capacity in mind.
Overall structure is the precondition for every other technical choice. Leaving it vague forces redoing every downstream decision: framework, language, headcount, cloud, test strategy, deployment — all depend on it.
For a single app vs functions split into integrated services, the engineer count and skill mix differ by an order of magnitude. What 5 people build as the former might need 20 as the latter.
“Build now, split later” is theoretically possible but essentially a rebuild in practice. Underestimating it hurts.
The three basic patterns
The application’s overall structure splits into three: monolith, microservices, modular monolith.
flowchart LR
subgraph MONO["Monolith"]
M1[All features<br/>1 code / 1 DB]
end
subgraph MODMONO["Modular Monolith"]
MM1[Module A]
MM2[Module B]
MM3[Module C]
MM1 -.|explicit boundary| MM2
MM2 -.| MM3
MM_DB[(Shared DB)]
MM1 --> MM_DB
MM2 --> MM_DB
MM3 --> MM_DB
end
subgraph MICRO["Microservices"]
MS1[Service A]
MS2[Service B]
MS3[Service C]
MS_DB1[(DB-A)]
MS_DB2[(DB-B)]
MS_DB3[(DB-C)]
MS1 --> MS_DB1
MS2 --> MS_DB2
MS3 --> MS_DB3
MS1 -.API/Event.- MS2
MS2 -.- MS3
end
classDef mono fill:#dbeafe,stroke:#2563eb;
classDef mod fill:#fef3c7,stroke:#d97706;
classDef micro fill:#fae8ff,stroke:#a21caf;
class MONO,M1 mono;
class MODMONO,MM1,MM2,MM3,MM_DB mod;
class MICRO,MS1,MS2,MS3,MS_DB1,MS_DB2,MS_DB3 micro;
| Pattern | Quick description |
|---|---|
| Monolith | Traditional: all features in one app. Simplest |
| Microservices | Each capability as an independent app, integrated over network |
| Modular Monolith | Looks like one app, internally split with explicit boundaries |
Microservices boomed in the 2010s; today there’s a swing back to modular monolith. The level-headed view now settling in: not new vs old — pick by scale and organization.
Monolith
True to “Monolith,” the most traditional structure: all features in one application. Login, payment, inventory all run on the same code, server, DB. From e-commerce to enterprise systems, much of the world’s software is still built this way.
At small/mid scale, it’s overwhelmingly the fastest to start, easiest for data consistency, easiest to debug. But at huge scale, builds get long, multi-person changes collide, and one feature’s bug stops the whole app.
| Pros | Cons |
|---|---|
| Overwhelmingly fast to start | Heavier development at huge scale |
| Easier data consistency | Multi-person change collision |
| Easier debugging | One feature’s outage stops the whole app |
| Simple operations / monitoring | Tech selection is unified across the whole |
Examples: Ruby on Rails apps, Spring Boot enterprise systems, WordPress.
“Monolith = old” is a misreading. It remains the best choice at small / mid scale.
Microservices
Microservices runs each capability as a small independent app, integrated over the network. For e-commerce, “User,” “Catalog,” “Payment,” “Shipping” become separate apps talking via APIs to form one product.
In the 2010s, Netflix and Amazon adopting it spread the model. You can split teams per capability and develop in parallel; you can scale parts. But operational complexity grows by orders of magnitude. As service count grows, just building monitoring, log aggregation, and distributed tracing becomes a project of its own.
| Pros | Cons |
|---|---|
| Per-capability parallel team development | Whole-system complexity skyrockets |
| Localize partial outages | Operational and monitoring cost explodes |
| Per-service tech choice | Inter-service communication degrades performance |
| Scale parts independently | Bug-cause identification is hard |
Examples: Amazon, Netflix, large-scale services.
Only viable with a large org and operational capacity. Small teams copying it almost always break.
Segment’s “Goodbye Microservices” (industry case)
The analytics platform Segment ran 140+ microservices from 2013, then in 2018 published “Goodbye Microservices” and consolidated back into a monolith. Service-chain failures, deployment coordination, exploding test environments — solvable individually but together unmanageable. The story circulated widely.
That a company with that level of engineering couldn’t manage it carries weight. Many remember the “finally, someone said it” wry tone in the industry at peak microservices fervor. Worth remembering this case before jumping at microservices because “Netflix uses them.”
Distribution is multiplicative, not additive. Doubling services means quadrupling integration patterns.
Modular Monolith
Modular Monolith looks like a monolith (one app) but uses explicit internal boundaries. Internally split into “User,” “Catalog,” “Payment” modules — modules don’t call each other freely; they only integrate through defined interfaces.
One app, so deployment and operations stay monolith-simple, while internal structure is clean — making future microservices carve-out cheap. Aiming at the “monolith’s operational simplicity” plus “microservices’ clean structure.”
| Pros | Cons |
|---|---|
| Operations as simple as monolith | Discipline required to enforce internal boundaries |
| Future split is comparatively easy | Without splitting, the value is thin |
| Less collision in large-team development | Slightly higher design difficulty |
| One DB makes consistency easy | Less parallelism than microservices |
The modern default. When in doubt, start here, then carve out partial microservices as needed. That’s the default.
Modular monolith is “the when-in-doubt” default. Splitting is always available later.
Three-way comparison
Lining up the three on key axes: monolith wins on “ease of start” and “ease of operations”; microservices wins on “parallel development” and “outage isolation.” Modular monolith sits in the middle with circle-or-better on most axes — a good balance.
| Aspect | Monolith | Modular Monolith | Micro services |
|---|---|---|---|
| Easy to start | ◎ | ○ | × |
| Low ops cost | ◎ | ○ | × |
| Large-scale parallel dev | × | △ | ◎ |
| Outage localization | × | △ | ◎ |
| Future split ease | × | ◎ | — |
| Required staff/skill | Few | Few-mid | Many |
| Learning cost | Low | Mid | High |
Microservices does “more” but the cost paid is overwhelmingly larger — that’s the right framing.
Decision criteria
1. Scale and team size
Team size is the largest factor. Adopting microservices with 1-10 people means not enough hands per service split; one person juggles multiple, and you only pay operational cost without the benefit.
Conversely, a 100+ person org maintaining a monolith means everyone touches the same codebase: change collisions, deploy contention, review delays. Splitting per capability lets teams work independently and raises overall productivity.
| Scale | Suitable form |
|---|---|
| A few to 10 people | Monolith overwhelmingly favorable |
| 10-30 | Modular monolith is safe |
| 30+, multi-site | Consider microservices |
Conway’s Law (the empirical observation that org structure reflects in architecture) plays out heavily. One team with microservices: no split benefit, just rising operational cost.
2. Can you operate it?
Microservices operations is harder than imagined. Even one app needs monitoring, logs, deploys; with 10-20 services, dedicated platforms (distributed tracing, service mesh: shared infrastructure handling inter-service traffic) for cross-service monitoring and incident tracking become mandatory.
Operating these requires at minimum a few dedicated infra people experienced with containers and distributed systems. “Functions are independent and that feels good” is the developer view; from operations, managed objects grow exponentially.
- Are there people who can build cross-service monitoring and logging?
- Is there container / distributed-systems operational experience?
- Is there capacity to chase bugs across multiple services?
If any of these is missing, don’t choose microservices. No exceptions.
3. Growth phase
Growth phase also shifts the optimal. At idea validation / MVP (Minimum Viable Product) stage, releasing fast and gathering market signal is the priority.
Default to monolith — fastest to build. Starting straight in microservices means infrastructure consumes the time meant for features, and the canonical failure pattern is “collapse at MVP stage”.
After PMF (Product Market Fit) and into expansion, with growing team, organize into modular monolith. Once the org grows past one app’s manageability, carve out the bottlenecked features as microservices.
| Phase | Recommended |
|---|---|
| Idea validation / MVP | Monolith for fastest release |
| Post-PMF expansion | Modular monolith to organize structure |
| After org grows | Carve out specific features as microservices |
Triggers for carving out from modular monolith
When should pieces be microservice-extracted from a modular monolith? Don’t rely on vague “when it gets big” — use numerical thresholds.
| Carve-out signal | Target |
|---|---|
| Dev team requests independence | 30+, multi-site |
| Specific feature receives 10x+ requests vs whole | Want to scale just the spike |
| Build time blocks development | 10+ minutes / deploy frequency drops |
| Specific feature’s SLO diverges sharply | Payment 99.99%, rest 99.9% |
| Want different language / runtime per part | ML inference Python, main TypeScript |
Carve out by “the bottleneck or independence-needing area,” not by feature. Splitting everything at once almost always fails.
Selection by case
Solo / startup MVP
Monolith only. Build speed is the priority; carrying operational complexity at a stage when users may not even arrive is suicidal. Pick frameworks like Ruby on Rails, Laravel, Django that “one person can build it all” for fastest path.
Mid-sized business apps (internal systems, early SaaS)
Modular monolith. With future feature growth expected, set internal boundaries from the start. When splitting becomes necessary, it’s overwhelmingly cheaper than splitting from a monolith.
Multi-team large-scale services with parallel development
Microservices. Per-team independent deploy / release pays off; benefits scale with the org. Investment in operational platforms is mandatory.
Existing monolith with one feature having performance issues
Carve just that feature into microservices. No need to rebuild everything. “Want to scale just notifications” -> carve out notifications, keep the rest.
Future expansion uncertain, currently small
Modular monolith. Going monolith first then splitting later is much costlier than starting modular and carving when needed.
Org × structure ladder
Note: industry rates as of April 2026. Periodic refresh required.
Including Conway’s Law, team size effectively decides recommended structure:
| Org phase | Headcount | Recommended | Typical service count | DB |
|---|---|---|---|---|
| MVP / individual | 1-3 | Monolith | 1 | 1 DB |
| Early startup | 3-10 | Monolith → Modular monolith | 1 | 1 DB |
| Mid-sized SaaS | 10-30 | Modular monolith | 1-3 | 1-2 DBs |
| Scale | 30-100 | Modular + partial microservices | 5-15 | Per use |
| Enterprise / multi-site | 100+ | Microservices (core) + modular monolith (operational) | 30-hundreds | Per service |
The practical floor for microservices: “30+ team + 5+ dedicated ops.” Below that, only inter-service traffic, distributed tracing, and data-consistency operational costs go up; dev speed drops.
Segment’s 2018 reorganization from 140 services back to a few symbolizes “out-of-scale microservices.”
Microservices is a 30+-person privilege. Copying it as a 10-person team almost certainly breaks.
Monolith-split / microservices traps
Common pitfalls in splitting an existing monolith. Stepping on these means splitting without getting the benefits — the worst outcome.
| Forbidden move | Why |
|---|---|
| Microservice the whole thing at once | As Segment learned in 2018, distribution complexity grows exponentially. Carve one at a time via Strangler Fig is the only realistic answer |
| Inter-service traffic via synchronous REST only | One service’s failure cascades. Combine async messaging (Kafka / SQS) |
| Share the DB during split | Tight coupling stays via shared DB; service independence’s benefit disappears. DB separation is the heart of splitting |
| Microservicing and language-mixing simultaneously | Five-language mixed maintenance scatters maintainers; nobody understands the whole |
| Distributed tracing / log aggregation added later | Required from day one of distribution. Without it, MTTR balloons to hours during incidents |
| Implementing distributed transactions without Saga / Outbox | Production gets double-payment, inventory mismatch. Distributed transactions need design specialty |
| Splitting ignoring Conway’s Law | ”One team touches multiple services” wastes the split. One team per service is the rule |
| Choosing microservices because “it’s modern” | Just one option; monolith is still active with many successful services. New != superior |
| Applying large-scale designs to small scale | Time meant for features gets eaten by operational setup; the project stops moving forward |
The standard path is “modular monolith → partial microservicing” starting past 10 people. Picking distribution from the start is “prepaying technical debt.”
Going back to monolith is harder than greenfield. Not running to distribution at initial selection is the largest defense.
AI decision axes
| AI-era favorable | AI-era unfavorable |
|---|---|
| Modular monolith (boundaries + whole-survey) | Excessive microservicing |
| Type sharing / monorepo (multiple projects in one repo) (tRPC, Turborepo) | gRPC (high-speed binary API) + manually managed Proto distribution |
| Explicit module boundaries / interfaces | Implicit dependencies, hidden API contracts |
| Single-stack typed (e.g. TypeScript) | Polyglot (multi-language) configurations |
- Confront org size and team count head-on (size determines candidates).
- Confirm operational capacity depth (monitoring, distributed tracing, incident tracking workable).
- Match growth phase (MVP must be monolith).
- AI affinity (one codebase for AI to survey).
What you must decide — what’s your project’s answer?
Articulate your project’s answer in 1-2 sentences for each:
- Initial release form (monolith / modular / micro)
- Internal module boundaries (by feature or by business domain)
- Database split or shared
- Future-split scenario set assumed
- Operational capacity (monitoring / deploy / incident) holds for the configuration
- Team org structure aligns with design (Conway’s Law)
- First feature to carve out if microservicing
Summary
This article covered software architecture’s overall structure — three patterns of monolith / microservices / modular monolith from scale, operational capacity, and growth phase.
When in doubt, modular monolith. MVPs always monolith. Microservices floor: 30+ + 5+ dedicated ops. The 2026 realistic answer including AI era.
The next article covers module design (Layered / Hexagonal / Onion / Clean).
Back to series TOC -> ‘Architecture Crash Course for the Generative-AI Era’: How to Read This Book
I hope you’ll read the next article as well.
📚 Series: Architecture Crash Course for the Generative-AI Era (19/89)