For most early-stage companies, the monolith vs microservices startup question has a short answer: build a modular monolith first. Split out services only when a measured limit forces it, such as a team too large to share one deploy or one component with very different scaling needs. Microservices suit a narrower group: companies with several independent teams, proven product-market fit and the budget for platform work.
This is not a purist position. Many teams run microservices well. But they usually got there by splitting a working monolith, not by starting with twelve services and three engineers.
What we mean by a modular monolith
A modular monolith is one deployable application with hard internal boundaries. Billing, accounts, notifications and search live in separate modules. Each module exposes a small public interface and owns its own tables.
It still deploys as one unit and usually runs on one database, often with a schema per module. You get one build, one log stream and one place to debug. Tools such as Packwerk for Rails, Nx module boundaries for TypeScript or ArchUnit for Java can fail the build when one module reaches into another's internals.
Microservices split those modules into separately deployed processes that talk over the network. Each service owns its data and ships on its own schedule. That independence is the whole point, and it is also the whole cost.
A 30-minute discovery call gets you a straight second opinion on your architecture before it hardens into something expensive to change.
The monolith vs microservices startup comparison, factor by factor
Performance
A monolith makes in-process function calls that take microseconds. A service call crosses the network, adding serialisation, retries and typically 1 to 10 milliseconds per hop inside one data centre. A request that touches five services pays that cost five times, plus the risk that any one of them is slow.
Microservices win when one component needs very different resources. A video transcoder or ML inference worker can scale on GPUs without dragging the rest of the app along. That is a case for extracting one service, not for splitting everything.
Developer availability
Almost every backend developer can work in a single Rails, Django, Laravel, Spring or Node codebase on day one. Fewer have run distributed systems in production: tracing across services, handling partial failures and versioning internal APIs. Those engineers exist, but they cost more and take longer to hire.
Onboarding differs too. In a monolith, a new hire clones one repo and runs it. With fifteen services, they often need Docker Compose, seeded data for several databases and a map of who calls whom.
Cost
A small monolith can run on two app instances, a managed Postgres database and a load balancer. For many early products that is a few hundred dollars a month. Microservices add a container platform, an API gateway, centralised logging and distributed tracing.
The cluster itself is cheap: an Amazon EKS control plane costs $0.10 per hour. The real cost is people. In our experience, a team running ten or more services typically spends 20 to 30 percent of engineering time on platform and integration work that a monolith would not need.
Time to market
Early on, your data model changes often. In a monolith, renaming a field is one pull request and one migration. Across services, it can mean a versioned API, a deploy sequence and a period of supporting both shapes.
For a startup still looking for product-market fit, that difference compounds every sprint. Speed of change matters more than independent deploys when you have one team and one product.
Ecosystem maturity
Both sides are mature now. Monolith frameworks have twenty years of conventions for auth, background jobs, migrations and testing. Microservices tooling has caught up, with Kubernetes, OpenTelemetry for tracing, and managed runtimes such as AWS Fargate and Google Cloud Run.
The difference is in assembly. A monolith framework hands you one integrated stack. Microservices hand you excellent parts that your team still has to wire together and own.
Long-term maintenance
This is where microservices earn their keep, at the right size. Once a shared codebase passes roughly 30 to 50 engineers, merge conflicts, slow test suites and risky deploys start to dominate. Independent services let each team own its code end to end.
Below that size, maintenance usually favours the monolith. You have one dependency upgrade, one security patch cycle and one CI pipeline. With twelve services, a framework upgrade becomes twelve upgrades.
Choose a monolith if, choose microservices if
Choose a modular monolith if
- You have fewer than about 15 to 20 engineers, or one team that ships together.
- You are still searching for product-market fit and expect your data model to change monthly.
- Scaling one stateless app horizontally would cover your expected load for the next 18 months.
Choose microservices if
- You have several teams that need to deploy on their own schedule without coordinating releases.
- One component has scaling, uptime or compliance needs that clearly differ from the rest, such as card data under PCI DSS.
- You already have, or can fund, platform engineers to own deployment, observability and service contracts.
There is an honest middle path. Many growing companies end up with a monolith plus two to five extracted services. That is a normal, healthy shape, not a failure to finish the job.
The outdated myth about each option
Monolith myth: monoliths cannot scale
A stateless monolith scales horizontally behind a load balancer like any other service. Shopify has publicly described running its core commerce platform as a large modular Rails monolith. The database usually becomes the bottleneck first, and that is true whichever architecture you pick.
Microservices myth: you need a big ops team to run them
That was true when everyone ran their own clusters on their own servers. Managed platforms like Cloud Run, Fargate and managed Kubernetes have removed much of that work. What they have not removed is the design work: service boundaries, data ownership, API versioning and debugging failures that span three services.
The mistake teams make most often
The most common failure is the distributed monolith. Teams split code into services but keep a shared database, synchronous call chains and coordinated releases. They pay the full network and operations cost of microservices and get none of the independence.
The warning signs are easy to spot:
- Two services must deploy in a set order.
- One schema change breaks three services.
- A single user request fans out into a chain of six blocking calls.
The opposite mistake is real too: a monolith with no internal boundaries. Every module reads every table, so splitting anything later means a rewrite. A modular monolith avoids both failures because the seams exist before you need them.
How to keep the option to split later
- One owner per table. Only the billing module writes to billing tables, and other modules call its public interface.
- Enforce boundaries in CI. Use Packwerk, Nx or ArchUnit so a forbidden import fails the build instead of relying on code review.
- Use events where order does not matter. An in-process event bus today can become a queue such as SQS or Kafka later with little change to callers.
- Keep the app stateless. Store sessions and files outside the process so you can add instances freely.
- Measure before splitting. Track per-module CPU, latency and deploy frequency so any extraction rests on data.
How we decide this for clients
We start with four questions:
- How many engineers will work on this in 12 months?
- Which parts of the system have different scaling, uptime or compliance needs?
- How often does the data model still change?
- Who will own the infrastructure after launch?
For most new products, the answers point to a modular monolith on a mainstream framework. We extract one or two services from day one only where the case is clear, such as payment handling, media processing or a fragile third-party integration. We document each module boundary so the developers who inherit the code know where the seams are.
When a client already runs microservices that slow them down, we do not default to merging everything. We map call chains and shared tables first, then merge the services that always change together. Often the right fix is fewer, larger services rather than a full return to one app.
FAQ
Should a startup use microservices from day one?
Usually not. Unless you already have several teams or one component with clearly different scaling or compliance needs, microservices slow early development. A modular monolith gives you speed now and clean seams to split later.
When should you move from a monolith to microservices?
Move when a measured problem appears: teams blocking each other's deploys, test suites too slow to run often, or one component that needs separate scaling. Extract one service at a time, starting with the module that has the clearest boundary. Headcount alone is not a trigger.
What is a modular monolith?
It is one deployable application divided into modules with enforced boundaries and separate data ownership. It runs and deploys like a monolith but is organised like a set of services. That makes later extraction a refactor rather than a rewrite.



