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.”
Before you read this
This article is mostly about development: programs and APIs. If IT vocabulary is unfamiliar, reading the primer "Programs and APIs" first makes it far easier to follow. You can also look anything up in the glossary as you read.
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.
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.
| 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 — still the best at small to mid scale
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 — a structure for parallel development in a large organisation
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” is the case everyone cites here.
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 — where to land when in doubt
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.
The three compared
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.
How to choose — three scenarios by scale
The deciding factor is Conway’s law — the empirical rule that the structure of the organisation determines the structure of the system. Team size and operational capacity settle the candidates almost entirely.
The criteria that actually decide it are these.
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 increasing 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. Which growth phase you are in. Growth phase also shifts the optimal. At idea validation / MVP 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 |
If it is a solo or startup MVP — monolith, no alternative
Speed of building comes first, and taking on operational complexity while you still do not know whether users will show up is suicidal. Starting straight into microservices is the classic failure: infrastructure work eats the time before any feature exists, and the project runs out of strength at the MVP stage. Aim for the fastest release with a framework one person can build all of — Rails, Laravel or Next.js.
If it is a small or mid-size SaaS or internal system — draw boundaries with a modular monolith
In the expansion phase after product-market fit, with a team of ten to thirty, make the internal boundaries explicit with a modular monolith. When a split does become necessary later, it is dramatically cheaper than splitting a monolith that had no boundaries. The point is to keep consistency in one database while allowing modules to talk only through interfaces.
If it is a large enterprise with parallel teams — microservices, with the ops investment
Only once you pass thirty people across multiple locations and genuinely want each team to deploy independently do the benefits of microservices exceed the cost. Even then, three things have to be in place: cross-service monitoring and log infrastructure, people with experience operating distributed systems, and a fault-tracing setup that works across services. If even one is missing, do not choose it — without exception.
The same question, framed case by case, lands like this.
Solo, or a 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 applications — 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.
Large-scale services with several teams developing in parallel. Microservices. Per-team independent deploy / release pays off; benefits scale with the org. Investment in operational platforms is mandatory.
An existing monolith where one feature has performance problems. 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, small today. Modular monolith. Going monolith first then splitting later is much costlier than starting modular and carving when needed.
A practical scale × 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, inter-service traffic, distributed tracing, and data-consistency operational costs just balloon; 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.
Triggers for carving a service out of a 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.
AI decision axes — Can the AI take in the whole codebase at once?
Once AI-driven development becomes the norm, “can AI survey the entire codebase” becomes an additional consideration for structure selection. Modular monolith is a structure that makes boundaries explicit within one codebase, making it easy for AI to grasp full context — earning reappraisal as the prime candidate for the AI era.
Microservices fragment AI’s context
Imagine 10 microservices. When you ask AI to “fix the order-processing bug,” if the order service, inventory service, and payment service code live in separate repos, AI can only view each independently. Inter-service message contracts and implicit dependencies often don’t appear in code, making it difficult for AI to produce fixes that maintain overall consistency.
With a modular monolith, AI can grasp the boundaries and dependencies of order, inventory, and payment modules all within one repository. Boundaries exist but context stays connected — this is the most productive structure for AI utilization.
Monorepo + type sharing combination
Even when adopting microservices, consolidating all services in a monorepo (Nx, Turborepo, etc.) and sharing type definitions dramatically improves AI utilization efficiency. With AI able to read the entire repository, it can detect type inconsistencies between services and API compatibility breaks.
Pitfalls and forbidden moves
Here are the six most dangerous of the traps that come up when deciding to split an existing monolith.
| Forbidden move | Why it is bad → what to do instead |
|---|---|
| Converting every feature to microservices at once | the complexity of distribution grows exponentially → carving out one at a time (Strangler Fig) is the only realistic answer |
| Keeping a shared database through the split | the tight coupling survives and the independence benefit disappears → separating the database is the real substance of a split |
| Building inter-service communication on synchronous REST alone | one service failing propagates to everything → combine it with asynchronous messaging (Kafka, SQS) |
| Adding distributed tracing and log aggregation afterwards | causes become unidentifiable during an incident and MTTR runs into hours → have them from day one of distribution |
| Implementing distributed transactions without knowing Saga or Outbox | double payments and stock inconsistencies appear in production → learn the design patterns first |
| Choosing on the thought-terminating “microservices means modern” | it is one option among several → choose calmly on scale, staffing and phase |
Starting the order — modular monolith, then partial microservices — from around ten people is the standard path. Going back to a monolith is harder than building one, so the strongest defence is not running to distribution in the first selection.
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
Write your answers down as an ADR. A concrete guide to writing them is here.
Related Articles
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.
Also popular with readers
📚 Series: Architecture Crash Course for the Generative-AI Era (25/95)