Software Architecture

Choosing Overall Structure — Monolith / Microservices / Modular Monolith

Choosing Overall Structure — Monolith / Microservices / Modular Monolith

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;
PatternQuick description
MonolithTraditional: all features in one app. Simplest
MicroservicesEach capability as an independent app, integrated over network
Modular MonolithLooks 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.

ProsCons
Overwhelmingly fast to startHeavier development at huge scale
Easier data consistencyMulti-person change collision
Easier debuggingOne feature’s outage stops the whole app
Simple operations / monitoringTech 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.

ProsCons
Per-capability parallel team developmentWhole-system complexity skyrockets
Localize partial outagesOperational and monitoring cost explodes
Per-service tech choiceInter-service communication degrades performance
Scale parts independentlyBug-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.”

ProsCons
Operations as simple as monolithDiscipline required to enforce internal boundaries
Future split is comparatively easyWithout splitting, the value is thin
Less collision in large-team developmentSlightly higher design difficulty
One DB makes consistency easyLess 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.

AspectMonolithModular
Monolith
Micro
services
Easy to start×
Low ops cost×
Large-scale parallel dev×
Outage localization×
Future split ease×
Required staff/skillFewFew-midMany
Learning costLowMidHigh

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.

ScaleSuitable form
A few to 10 peopleMonolith overwhelmingly favorable
10-30Modular monolith is safe
30+, multi-siteConsider 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.

PhaseRecommended
Idea validation / MVPMonolith for fastest release
Post-PMF expansionModular monolith to organize structure
After org growsCarve 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 signalTarget
Dev team requests independence30+, multi-site
Specific feature receives 10x+ requests vs wholeWant to scale just the spike
Build time blocks development10+ minutes / deploy frequency drops
Specific feature’s SLO diverges sharplyPayment 99.99%, rest 99.9%
Want different language / runtime per partML 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 phaseHeadcountRecommendedTypical service countDB
MVP / individual1-3Monolith11 DB
Early startup3-10Monolith → Modular monolith11 DB
Mid-sized SaaS10-30Modular monolith1-31-2 DBs
Scale30-100Modular + partial microservices5-15Per use
Enterprise / multi-site100+Microservices (core) + modular monolith (operational)30-hundredsPer 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 moveWhy
Microservice the whole thing at onceAs 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 onlyOne service’s failure cascades. Combine async messaging (Kafka / SQS)
Share the DB during splitTight coupling stays via shared DB; service independence’s benefit disappears. DB separation is the heart of splitting
Microservicing and language-mixing simultaneouslyFive-language mixed maintenance scatters maintainers; nobody understands the whole
Distributed tracing / log aggregation added laterRequired from day one of distribution. Without it, MTTR balloons to hours during incidents
Implementing distributed transactions without Saga / OutboxProduction 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 scaleTime 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 favorableAI-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 / interfacesImplicit dependencies, hidden API contracts
Single-stack typed (e.g. TypeScript)Polyglot (multi-language) configurations
  1. Confront org size and team count head-on (size determines candidates).
  2. Confirm operational capacity depth (monitoring, distributed tracing, incident tracking workable).
  3. Match growth phase (MVP must be monolith).
  4. 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.